Repository: vertcoin-project/one-click-miner-vnext Branch: master Commit: 3223da786213 Files: 98 Total size: 312.5 KB Directory structure: gitextract_6sylzzmb/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── 1-bug.yml │ │ └── 2-feature_request.yml │ └── workflows/ │ └── golangci-lint.yml ├── .gitignore ├── LICENSE ├── README.md ├── backend/ │ ├── backend.go │ ├── checks.go │ ├── languages.go │ ├── mining.go │ ├── p2pool.go │ ├── settings.go │ ├── tracking.go │ ├── update.go │ └── wallet.go ├── build.bat ├── build.sh ├── frontend/ │ ├── .gitignore │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── package.json.md5 │ ├── src/ │ │ ├── App.vue │ │ ├── assets/ │ │ │ └── css/ │ │ │ └── main.css │ │ ├── components/ │ │ │ ├── Checks.vue │ │ │ ├── Mining.vue │ │ │ ├── Send.vue │ │ │ ├── Settings.vue │ │ │ ├── TabBar.vue │ │ │ ├── Tracking.vue │ │ │ ├── Update.vue │ │ │ └── Welcome.vue │ │ ├── i18n/ │ │ │ ├── README.md │ │ │ ├── bg.json │ │ │ ├── da.json │ │ │ ├── de.json │ │ │ ├── en.json │ │ │ ├── es.json │ │ │ ├── fr.json │ │ │ ├── hi.json │ │ │ ├── hr.json │ │ │ ├── it.json │ │ │ ├── ja.json │ │ │ ├── lt.json │ │ │ ├── nl.json │ │ │ ├── no.json │ │ │ ├── pa.json │ │ │ ├── pl.json │ │ │ ├── pt.json │ │ │ ├── ro.json │ │ │ ├── ru.json │ │ │ ├── sl.json │ │ │ ├── sv.json │ │ │ ├── tr.json │ │ │ └── zh.json │ │ └── main.js │ └── vue.config.js ├── go.mod ├── go.sum ├── keyfile/ │ └── keyfile.go ├── logging/ │ └── log.go ├── main.go ├── miners/ │ ├── ccminer.go │ ├── cryptodredge.go │ ├── lyclminer.go │ ├── miners.go │ ├── teamredminer.go │ └── verthashminer.go ├── miners.json ├── networks/ │ └── networks.go ├── p2pool_nodes.json ├── ping/ │ └── ping.go ├── pools/ │ ├── miningpoolsweden.go │ ├── p2pool.go │ ├── p2proxy.go │ ├── pool.go │ ├── suprnova.go │ ├── zergpool.go │ └── zpool.go ├── prerequisites/ │ ├── amdgpudriver.go │ ├── msvcrt2013.go │ ├── nvidiadriver.go │ └── prerequisites.go ├── project.json ├── tracking/ │ ├── matomo.go │ └── version.go ├── util/ │ ├── autostart.go │ ├── bech32/ │ │ └── bech32.go │ ├── gpus.go │ ├── gpus_test.go │ ├── miners_linux.go │ ├── miners_windows.go │ ├── util.go │ ├── versioncheck.go │ └── versioncheck_test.go └── wallet/ ├── signsend.go ├── sigops.go └── wallet.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/1-bug.yml ================================================ name: Bug report description: Submit a new bug report. labels: [bug] body: - type: markdown attributes: value: | ## This issue tracker is only for technical issues related to Vertcoin OCM. * General Vertcoin questions and/or support requests should reach out in one of the Vertcoin forums. See more here https://vertcoin.org/community/. * For reporting security issues, please reach out to the project maintainers or other members of the Vertcoin Github Organization ---- - type: checkboxes attributes: label: Is there an existing issue for this? description: Please search to see if an issue already exists for the bug you encountered. options: - label: I have searched the existing issues required: true - type: checkboxes attributes: label: Did you read [Common issues and fixes](https://github.com/vertcoin-project/one-click-miner-vnext/issues/618)?. options: - label: Yes / Not relevant required: true - type: textarea id: current-behaviour attributes: label: Current behaviour description: Tell us what went wrong. Add pictures if relevant. validations: required: true - type: textarea id: expected-behaviour attributes: label: Expected behaviour description: Tell us what you expected to happen validations: required: true - type: textarea id: reproduction-steps attributes: label: Steps to reproduce description: | Tell us how to reproduce your bug. Please attach related screenshots if necessary. * Run-time or compile-time configuration options * Actions taken validations: required: true - type: textarea id: logs attributes: label: Relevant log output description: | Please copy and paste any relevant log output or attach a debug.log file. You can find the debug.log in your OCM's data directory: Windows: `%appdata%\vertcoin-ocm`or Linux: `.vertcoin-ocm` Please be aware that the debug log might contain identifying information. validations: required: false - type: dropdown attributes: label: How did you obtain Vertcoin OCM? multiple: false options: - Pre-built binaries - Compiled from source - Other validations: required: true - type: input id: ocm-version attributes: label: What version of Vertcoin OCM are you using? description: When OCM is running, the version is listed at the bottom. placeholder: e.g. v2.2-beta3 or master@e1bf547 validations: required: true - type: input id: os attributes: label: Operating system and version placeholder: e.g. "Windows 10" or "Ubuntu 22.04 LTS" validations: required: true - type: textarea id: machine-specs attributes: label: Machine specifications description: | What are the specifications of the host machine? e.g. CPU and GPU If building OCM from source please supply used versions of required dependencies. eg. GO version, NPM version and NodeJS version. For the GUI-related issue on Linux provide names and versions of a distro, a desktop environment and a graphical shell (if relevant). validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/2-feature_request.yml ================================================ name: Feature Request description: Suggest an idea for this project. labels: [feature] body: - type: checkboxes attributes: label: Is there an existing feature request for this? description: Please search to see if a feature request already exists. options: - label: I have searched for existing feature requests required: true - type: textarea id: feature attributes: label: Please describe the feature you'd like to see added. description: Attach screenshots or logs if applicable. validations: required: true - type: textarea id: related-problem attributes: label: Is your feature related to a problem, if so please describe it. description: Attach screenshots or logs if applicable. validations: required: false - type: textarea id: solution attributes: label: Describe the solution you'd like validations: required: false - type: textarea id: alternatives attributes: label: Describe any alternatives you've considered validations: required: false - type: textarea id: additional-context attributes: label: Please leave any additional context validations: required: false ================================================ FILE: .github/workflows/golangci-lint.yml ================================================ name: golangci-lint on: push: branches: - master pull_request: branches: - master permissions: contents: read # Optional: allow read access to pull request. Use with `only-new-issues` option. # pull-requests: read env: GOCACHE: /home/runner/work/go/pkg/build GOPATH: /home/runner/work/go jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Go uses: actions/setup-go@v5 with: go-version: '1.22.0' # The Go version to download (if necessary) and use. # Install all the dependencies - name: Install dependencies run: | go mod tidy - name: golangci-lint uses: golangci/golangci-lint-action@v4 with: # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version version: v1.56.2 # Run build of the application - name: Run build run: go build . ================================================ FILE: .gitignore ================================================ vertcoin-ocm *.zip .vscode/ vertcoin-ocm.exe *.code-workspace *.exe *.manifest *.syso ================================================ FILE: LICENSE ================================================ Copyright (C) 2019-2021 Gert-Jaap Glasbergen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # One-Click Miner The One-Click Miner allows cryptocurrency enthusiasts to get into mining with minimal effort. When you download the One-Click Miner, you will be asked to provide a password for your (built in) wallet. It will then immediately commence mining. This is a redevelopment of Vertcoin's [One Click Miner](https://github.com/vertcoin-project/one-click-miner). This software is available for Windows and Linux. ## FAQ ### Which GPUs are supported? Please refer to this list of [supported hardware.](https://github.com/CryptoGraphics/VerthashMiner#supported-hardware) ### I have an error message that reads 'Failure to configure' or 'Checks failed' You may need to add an exclusion to your antivirus / Windows Defender. The data directory is located at `%AppData%\vertcoin-ocm` on Windows or `~/.vertcoin-ocm` on Linux. ### My GPU is supported but an error messages reads 'no compatible GPUs' Update your GPU drivers to the latest version. ### My GPU is not being utilized The OCM is using all of your GPU resources. To verify, check CUDA usage for Nvidia or Compute 0/1 usage for AMD. ## Building The GUI of this MVP is based on [Wails](https://wails.app) and [Go](https://golang.org/). Install the Wails [prerequisites](https://wails.app/gettingstarted/) for your platform, and then run: ```bash go get github.com/wailsapp/wails/cmd/wails ``` Then clone this repository, and inside its main folder, execute: ```bash wails build ``` ## Donations If you want to support the further development of the One Click Miner, feel free to donate Vertcoin to [Vmnbtn5nnNbs1otuYa2LGBtEyFuarFY1f8](https://insight.vertcoin.org/address/Vmnbtn5nnNbs1otuYa2LGBtEyFuarFY1f8). ================================================ FILE: backend/backend.go ================================================ package backend import ( "path/filepath" "github.com/tidwall/buntdb" "github.com/btcsuite/btcd/wire" "github.com/vertcoin-project/one-click-miner-vnext/miners" "github.com/vertcoin-project/one-click-miner-vnext/pools" "github.com/vertcoin-project/one-click-miner-vnext/util" "github.com/vertcoin-project/one-click-miner-vnext/wallet" "github.com/wailsapp/wails" ) type Backend struct { runtime *wails.Runtime wal *wallet.Wallet settings *buntdb.DB pendingSweep []*wire.MsgTx minerBinaries []*miners.BinaryRunner rapidFailures []*miners.BinaryRunner pool pools.Pool refreshBalanceChan chan bool refreshHashChan chan bool refreshRunningState chan bool stopMonitoring chan bool stopHash chan bool stopBalance chan bool stopUpdate chan bool stopRunningState chan bool prerequisiteInstall chan bool alreadyRunning bool p2poolNodeSelected bool } func NewBackend(alreadyRunning bool) (*Backend, error) { backend := &Backend{ refreshBalanceChan: make(chan bool), refreshHashChan: make(chan bool), refreshRunningState: make(chan bool), stopHash: make(chan bool), stopBalance: make(chan bool), stopRunningState: make(chan bool), stopMonitoring: make(chan bool), stopUpdate: make(chan bool), prerequisiteInstall: make(chan bool), minerBinaries: []*miners.BinaryRunner{}, rapidFailures: []*miners.BinaryRunner{}, } if alreadyRunning { backend.alreadyRunning = true return backend, nil } db, err := buntdb.Open(filepath.Join(util.DataDirectory(), "settings.db")) if err != nil { return nil, err } backend.settings = db return backend, nil } func (m *Backend) ResetPool() { m.pool = pools.GetPool(m.GetPool(), m.Address(), m.GetTestnet()) } func (m *Backend) WailsInit(runtime *wails.Runtime) error { // Save runtime m.runtime = runtime go m.PrerequisiteProxyLoop() go m.UpdateLoop() return nil } func (m *Backend) OpenDownloadUrl(url string) { util.OpenBrowser(url) } func (m *Backend) AlreadyRunning() bool { return m.alreadyRunning } func (m *Backend) Close() { m.runtime.Window.Close() } ================================================ FILE: backend/checks.go ================================================ package backend import ( "fmt" "math/rand" "path/filepath" "runtime" "time" verthash "github.com/gertjaap/verthash-go" "github.com/vertcoin-project/one-click-miner-vnext/logging" "github.com/vertcoin-project/one-click-miner-vnext/miners" "github.com/vertcoin-project/one-click-miner-vnext/networks" "github.com/vertcoin-project/one-click-miner-vnext/tracking" "github.com/vertcoin-project/one-click-miner-vnext/util" ) func (m *Backend) PerformChecks() string { m.runtime.Events.Emit("checkStatus", "rapidfail") if len(m.rapidFailures) > 0 { m.runtime.Events.Emit("checkStatus", "Failed") m.rapidFailures = make([]*miners.BinaryRunner, 0) // Clear the failures return "Rapid failures: Your GPU is likely incompatible; check FAQ for supported hardware. If compatible, GPU overclocks or antivirus may be the cause." } m.runtime.Events.Emit("checkStatus", "compatibility") err := m.CheckGPUCompatibility() if err != nil { tracking.Track(tracking.TrackingRequest{ Category: "PerformChecks", Action: "CheckGPUCompatibilityError", Name: err.Error(), }) m.runtime.Events.Emit("checkStatus", "Failed") return err.Error() } m.runtime.Events.Emit("checkStatus", "installing_miners") err = m.InstallMinerBinaries() if err != nil { tracking.Track(tracking.TrackingRequest{ Category: "PerformChecks", Action: "InstallMinerBinariesError", Name: err.Error(), }) m.runtime.Events.Emit("checkStatus", "Failed") return err.Error() } m.runtime.Events.Emit("checkStatus", "verthash") verthashFile := filepath.Join(util.DataDirectory(), "verthash.dat") doneChan := make(chan bool, 1) progress := make(chan float64, 1) go func() { if m.GetSkipVerthashExtendedVerify() { err = verthash.MakeVerthashDatafileIfNotExistsWithProgress(verthashFile, progress) } else { err = verthash.EnsureVerthashDatafileWithProgress(verthashFile, progress) } doneChan <- true }() for { done := false select { case done = <-doneChan: break case prog := <-progress: m.runtime.Events.Emit("verthashProgress", prog*100) break } if done { break } } if err != nil { errorString := fmt.Sprintf("Failed to create or verify Verthash data file: %s", err.Error()) m.runtime.Events.Emit("checkStatus", "Failed") return errorString } for !m.p2poolNodeSelected { time.Sleep(time.Second) } for networks.Active.OCMBackend == "" { time.Sleep(500 * time.Millisecond) } args := m.GetArgs() for _, br := range m.minerBinaries { err := br.MinerImpl.Configure(args) if err != nil { errorString := fmt.Sprintf("Failure to configure %s: The data directory may have to be excluded from your antivirus. Check FAQ.", br.MinerBinary.MainExecutableName) tracking.Track(tracking.TrackingRequest{ Category: "PerformChecks", Action: "ConfigureError", Name: errorString, }) m.runtime.Events.Emit("checkStatus", "Failed") return errorString } if br.MinerImpl.AvailableGPUs() == 0 { m.runtime.Events.Emit("checkStatus", "Failed") return "Miner software reported no compatible GPUs. Check FAQ for supported hardware and ensure your GPU drivers are up to date." } } tracking.Track(tracking.TrackingRequest{ Category: "PerformChecks", Action: "Success", }) return "ok" } func (m *Backend) CheckGPUCompatibility() error { gpus := util.GetGPUs() compat := 0 gpustring := "" for _, g := range gpus { if g.Type != util.GPUTypeOther { compat++ } if gpustring != "" { gpustring += " / " } gpustring += g.OSName } tracking.Track(tracking.TrackingRequest{ Category: "EnumerateGPUs", Action: "Success", Name: gpustring, }) if compat == 0 { return fmt.Errorf("No compatible GPUs detected\n\nGPUs Found:\n%s - Check FAQ for supported hardware and ensure your GPU drivers are up to date.", gpustring) } return nil } func (m *Backend) CreateMinerBinaries() ([]*miners.BinaryRunner, error) { binaries := miners.GetMinerBinaries() gpus := util.GetGPUs() closedSource := m.GetClosedSource() testnet := m.GetTestnet() brs := []*miners.BinaryRunner{} for _, b := range binaries { match := false if b.Platform == runtime.GOOS { for _, g := range gpus { if g.Type == b.GPUType { if b.ClosedSource == closedSource { if b.Testnet == testnet { match = true } } } } } if match { if b.MultiGPUMiner { alreadyPresent := false for _, br := range brs { if br.MinerBinary.MainExecutableName == b.MainExecutableName { alreadyPresent = true break } } if alreadyPresent { logging.Debugf("Not adding already present multi-gpu binary [%s] again\n", b.MainExecutableName) continue } } logging.Debugf("Found compatible binary [%s] for [%s/%d] (Closed source: %t)\n", b.MainExecutableName, b.Platform, b.GPUType, b.ClosedSource) br, err := miners.NewBinaryRunner(b, m.prerequisiteInstall) if err != nil { return nil, err } br.Debug = m.GetDebugging() brs = append(brs, br) } else { logging.Debugf("Found incompatible binary [%s] for [%s/%d] (Closed source: %t)\n", b.MainExecutableName, b.Platform, b.GPUType, b.ClosedSource) } } if len(brs) == 0 { return nil, fmt.Errorf("Could not find compatible miner binaries - Check FAQ for supported hardware and ensure your GPU drivers are up to date.") } return brs, nil } func (m *Backend) InstallMinerBinaries() error { var err error m.minerBinaries, err = m.CreateMinerBinaries() if err != nil { return err } for _, br := range m.minerBinaries { err := br.Install() if err != nil { return err } } return nil } // Will be run at startup // Additionally it can be run if the backend returns an error after startup func (m *Backend) BackendServerSelector() { // Pick a random backend off the list n := rand.Intn(len(networks.Active.BackendServers)) // Run a simple check to see if the backend is up and returned data isn't nonsense // If the backend is bad, go through the list until a suitable one is found for range networks.Active.BackendServers { b := util.CheckBackendStatus(networks.Active.BackendServers[n]) if b { // If backend is up and return data other than 0, save it in networks.Active networks.Active.OCMBackend = networks.Active.BackendServers[n] logging.Infof("Using backend: %s\n", networks.Active.OCMBackend) return } n += 1 if n == len(networks.Active.BackendServers) { n = 0 } } // We'll only ever get here if all backends are unreachable.. networks.Active.OCMBackend = networks.Active.BackendServers[0] logging.Errorf("No working backend could be found..\n") } ================================================ FILE: backend/languages.go ================================================ package backend import ( "github.com/cloudfoundry/jibber_jabber" "github.com/vertcoin-project/one-click-miner-vnext/logging" "golang.org/x/text/language" ) var availableLanguages = []string{ // First language is default. So // alphabetical order except for // this one "en", "bg", "da", "de", "es", "fr", "hi", "hr", "it", "ja", "lt", "nl", "no", "pa", "pl", "pt", "ro", "ru", "sl", "sv", "tr", "zh", } var languageMatcher language.Matcher func init() { tags := []language.Tag{} for _, l := range availableLanguages { t, err := language.Parse(l) if err == nil { tags = append(tags, t) } } languageMatcher = language.NewMatcher(tags) } func (m *Backend) GetLocale() string { userLanguage, err := jibber_jabber.DetectIETF() if err != nil { logging.Warnf("Could not determine locale, defaulting to English: %s", err.Error()) return "en" } logging.Infof("User IETF is %s", userLanguage) userTag, err := language.Parse(userLanguage) if err != nil { logging.Warnf("Could not parse user IETF: %s", err.Error()) } tag, _, _ := languageMatcher.Match(userTag) logging.Infof("Matched tag is %s", tag.String()) base, _ := tag.Base() logging.Infof("Returning locale %s", base.String()) return base.String() } ================================================ FILE: backend/mining.go ================================================ package backend import ( "fmt" "time" "github.com/vertcoin-project/one-click-miner-vnext/logging" "github.com/vertcoin-project/one-click-miner-vnext/miners" "github.com/vertcoin-project/one-click-miner-vnext/tracking" "github.com/vertcoin-project/one-click-miner-vnext/util" ) func (m *Backend) GetArgs() miners.BinaryArguments { tracking.Track(tracking.TrackingRequest{ Category: "Mining", Action: "Switch Pool", Name: fmt.Sprintf("%v", m.pool.GetName()), }) return miners.BinaryArguments{ StratumUrl: m.pool.GetStratumUrl(), StratumUsername: m.pool.GetUsername(), StratumPassword: m.pool.GetPassword(), EnableIntegrated: m.getSetting("enableIntegrated"), } } func (m *Backend) GetPoolFee() string { return fmt.Sprintf("%0.2f%%", m.pool.GetFee()) } func (m *Backend) GetPoolName() string { return m.pool.GetName() } func (m *Backend) PayoutInformation() { m.pool.OpenBrowserPayoutInfo(m.Address()) } func (m *Backend) StartMining() bool { logging.Infof("Starting mining process...") tracking.Track(tracking.TrackingRequest{ Category: "Mining", Action: "Start", }) args := m.GetArgs() startProcessMonitoring := make(chan bool) go func() { <-startProcessMonitoring continueLoop := true for continueLoop { newMinerBinaries := make([]*miners.BinaryRunner, 0) for _, br := range m.minerBinaries { if br.CheckRunning() == miners.RunningStateRapidFail { m.rapidFailures = append(m.rapidFailures, br) m.runtime.Events.Emit("minerRapidFail", br.MinerBinary.MainExecutableName) } else { newMinerBinaries = append(newMinerBinaries, br) } } m.minerBinaries = newMinerBinaries select { case <-m.stopMonitoring: continueLoop = false case <-time.After(time.Second): } } logging.Infof("Stopped monitoring thread") }() go func() { cycles := 0 nhr := util.GetNetHash() th := util.GetTipHeight() var avgEarning float64 continueLoop := true for continueLoop { cycles++ if cycles > 600 { // Don't refresh this every time since we refresh it every second // and this pulls from Insight. Every 600s is fine (~every 4 blocks) nhr = util.GetNetHash() th = util.GetTipHeight() cycles = 0 } hr := uint64(0) for _, br := range m.minerBinaries { hr += br.HashRate() } hashrate := float64(hr) / float64(1000) hashrateUnit := "kH/s" if hashrate > 1000 { hashrate /= 1000 hashrateUnit = "MH/s" } if hashrate > 1000 { hashrate /= 1000 hashrateUnit = "GH/s" } if hashrate > 1000 { hashrate /= 1000 hashrateUnit = "TH/s" } m.runtime.Events.Emit("hashRate", fmt.Sprintf("%0.2f %s", hashrate, hashrateUnit)) netHash := float64(nhr) / float64(1000000000) m.runtime.Events.Emit("networkHashRate", fmt.Sprintf("%0.2f %s", netHash, hashrateUnit)) // Avoids wrong estimates when the backend is unavailable if th != 0 && nhr != 0 { coinsPerDay := util.GetCoinsPerDay(th) avgEarning = float64(hr) / float64(nhr) * float64(coinsPerDay) } m.runtime.Events.Emit("avgEarnings", fmt.Sprintf("%0.2f VTC", avgEarning)) select { case <-m.stopHash: continueLoop = false case <-m.refreshHashChan: case <-time.After(time.Second): } } }() go func() { continueLoop := true var pb uint64 for continueLoop { m.wal.Update() b, bi := m.wal.GetBalance() m.runtime.Events.Emit("balance", fmt.Sprintf("%0.8f", float64(b)/float64(100000000))) m.runtime.Events.Emit("balanceImmature", fmt.Sprintf("%0.8f", float64(bi)/float64(100000000))) logging.Infof("Updating pending pool payout...") newPb := m.pool.GetPendingPayout() pb = newPb m.runtime.Events.Emit("balancePendingPool", fmt.Sprintf("%0.8f", float64(pb)/float64(100000000))) select { case <-m.stopBalance: continueLoop = false case <-m.refreshBalanceChan: case <-time.After(time.Minute * 5): } } }() go func() { continueLoop := true for continueLoop { runningProcesses := 0 for _, br := range m.minerBinaries { if br.IsRunning() { runningProcesses++ } } m.runtime.Events.Emit("runningMiners", runningProcesses) timeout := time.Second * 1 if runningProcesses > 0 { timeout = time.Second * 10 } select { case <-m.stopRunningState: continueLoop = false case <-m.refreshRunningState: case <-time.After(timeout): } } }() for _, br := range m.minerBinaries { err := br.Start(args) if err != nil { m.StopMining() logging.Errorf("Failure to start %s: %s\n", br.MinerBinary.MainExecutableName, err.Error()) return false } } startProcessMonitoring <- true return true } func (m *Backend) RefreshBalance() { m.refreshBalanceChan <- true } func (m *Backend) RefreshHashrate() { m.refreshHashChan <- true } func (m *Backend) RefreshRunningState() { m.refreshRunningState <- true } func (m *Backend) StopMining() bool { tracking.Track(tracking.TrackingRequest{ Category: "Mining", Action: "Stop", }) select { case m.stopMonitoring <- true: default: } logging.Infof("Stopping mining process...") for _, br := range m.minerBinaries { err := br.Stop() if err != nil { logging.Errorf("Error stopping miner: %s %v", br.MinerBinary.MainExecutableName, err) } } select { case m.stopBalance <- true: default: } select { case m.stopHash <- true: default: } select { case m.stopRunningState <- true: default: } return true } ================================================ FILE: backend/p2pool.go ================================================ package backend import ( "github.com/vertcoin-project/one-click-miner-vnext/logging" "github.com/vertcoin-project/one-click-miner-vnext/ping" ) func (m *Backend) SelectP2PoolNode() { logging.Infof("Finding best P2Pool node...") ping.GetSelectedNode(m.getSetting("testnet")) logging.Infof("Found best P2Pool node: %v", ping.Selected.P2PoolURL) m.p2poolNodeSelected = true } ================================================ FILE: backend/settings.go ================================================ package backend import ( "fmt" "math/rand" "strconv" "github.com/tidwall/buntdb" "github.com/vertcoin-project/one-click-miner-vnext/logging" "github.com/vertcoin-project/one-click-miner-vnext/networks" "github.com/vertcoin-project/one-click-miner-vnext/pools" "github.com/vertcoin-project/one-click-miner-vnext/tracking" "github.com/vertcoin-project/one-click-miner-vnext/util" ) func (m *Backend) getSetting(name string) bool { setting := "0" err := m.settings.View(func(tx *buntdb.Tx) error { v, err := tx.Get(name) setting = v return err }) if err != nil { logging.Errorf("Error in getSetting(%s): %v", name, err) } return setting == "1" } func (m *Backend) setSetting(name string, value bool) { setting := "0" if value { setting = "1" } err := m.settings.Update(func(tx *buntdb.Tx) error { _, _, err := tx.Set(name, setting, nil) return err }) if err != nil { logging.Errorf("Error in setSetting(%s): %v", name, err) } } func (m *Backend) setIntSetting(name string, value int) { setting := fmt.Sprintf("%d", value) err := m.settings.Update(func(tx *buntdb.Tx) error { _, _, err := tx.Set(name, setting, nil) return err }) if err != nil { logging.Errorf("Error in setSetting(%s): %v", name, err) } } func (m *Backend) getIntSetting(name string) int { setting := "0" err := m.settings.View(func(tx *buntdb.Tx) error { v, err := tx.Get(name) setting = v return err }) if err != nil { logging.Errorf("Error in getSetting(%s): %v", name, err) } i, _ := strconv.Atoi(setting) return i } func (m *Backend) GetPool() int { pool := m.getIntSetting("pool") if pool == 0 { if m.GetTestnet() { return 2 // Default P2Pool on testnet } // Default to a random pool pools := pools.GetPools(m.Address(), m.GetTestnet()) pool := pools[rand.Intn(len(pools))].GetID() // Save this setting immediately so that we don't get // a different random pool in future calls to GetPool(). m.setIntSetting("pool", pool) return pool } return pool } func (m *Backend) SetPool(pool int) { if m.GetPool() != pool { m.setIntSetting("pool", pool) m.ResetPool() logging.Infof("Calling WalletInitialized\n") m.WalletInitialized() logging.Infof("Done!") } } func (m *Backend) SetEnableIntegrated(enabled bool) { m.setSetting("enableIntegrated", enabled) } func (m *Backend) GetEnableIntegrated() bool { return m.getSetting("enableIntegrated") } type PoolChoice struct { ID int `json:"id"` Name string `json:"name"` } func (m *Backend) GetPools() []PoolChoice { pc := make([]PoolChoice, 0) for _, p := range pools.GetPools(m.Address(), m.GetTestnet()) { pc = append(pc, PoolChoice{ ID: p.GetID(), Name: fmt.Sprintf("%s (%0.2f%% fee)", p.GetName(), p.GetFee()), }) } return pc } func (m *Backend) GetTestnet() bool { return false // Testnet is not necessary - return false //return m.getSetting("testnet") } func (m *Backend) SetTestnet(newTestnet bool) { if m.GetTestnet() != newTestnet { logging.Infof("Setting testnet to [%b]\n", newTestnet) m.setSetting("testnet", newTestnet) logging.Infof("Setting network to testnet=%b\n", newTestnet) networks.SetNetwork(newTestnet) logging.Infof("Calling WalletInitialized\n") m.WalletInitialized() logging.Infof("Done!") } } func (m *Backend) GetSkipVerthashExtendedVerify() bool { return false // Verification is default - return false //return m.getSetting("skipverthashverify") } func (m *Backend) SetSkipVerthashExtendedVerify(newVerthashVerify bool) { logging.Infof("Setting skip verthash verify to [%b]\n", newVerthashVerify) m.setSetting("skipverthashverify", newVerthashVerify) } func (m *Backend) GetClosedSource() bool { return false // No closed source Verthash miners - return false //return m.getSetting("closedsource") } func (m *Backend) SetClosedSource(newClosedSource bool) { logging.Infof("Setting closed source to [%b]\n", newClosedSource) m.setSetting("closedsource", newClosedSource) } func (m *Backend) GetDebugging() bool { return m.getSetting("debugging") } func (m *Backend) SetDebugging(newDebugging bool) { logging.Infof("Setting debugging to [%b]\n", newDebugging) m.setSetting("debugging", newDebugging) } func (m *Backend) GetAutoStart() bool { return util.GetAutoStart() } func (m *Backend) SetAutoStart(newAutoStart bool) { util.SetAutoStart(newAutoStart) } func (m *Backend) GetVersion() string { return tracking.GetVersion() } func (m *Backend) PrerequisiteProxyLoop() { for pi := range m.prerequisiteInstall { send := "0" if pi { send = "1" } m.runtime.Events.Emit("prerequisiteInstall", send) } } ================================================ FILE: backend/tracking.go ================================================ package backend import ( "github.com/vertcoin-project/one-click-miner-vnext/tracking" "github.com/vertcoin-project/one-click-miner-vnext/util" ) func (m *Backend) EnableTracking() { tracking.Enable() } func (m *Backend) DisableTracking() { tracking.Disable() } func (m *Backend) TrackingEnabled() string { if tracking.IsEnabled() { return "1" } return "0" } func (m *Backend) ReportIssue() { util.OpenBrowser("https://github.com/vertcoin-project/one-click-miner-vnext/issues/new?assignees=&labels=bug&projects=&template=1-bug.yml") } ================================================ FILE: backend/update.go ================================================ package backend import ( "time" "github.com/vertcoin-project/one-click-miner-vnext/tracking" "github.com/vertcoin-project/one-click-miner-vnext/util" ) func (m *Backend) UpdateAvailable() bool { r, _ := util.GetLatestRelease() lastVersion := util.VersionStringToNumeric(r.Tag) myVersion := util.VersionStringToNumeric(tracking.GetVersion()) return lastVersion > myVersion } func (m *Backend) VersionDetails() []string { r, _ := util.GetLatestRelease() return []string{r.Tag, r.Body, r.URL} } func (m *Backend) UpdateLoop() { for { stopUpdate := false select { case stopUpdate = <-m.stopUpdate: case <-time.After(time.Second * 15): } if stopUpdate { break } m.runtime.Events.Emit("updateAvailable", m.UpdateAvailable()) } } ================================================ FILE: backend/wallet.go ================================================ package backend import ( "fmt" "github.com/vertcoin-project/one-click-miner-vnext/keyfile" "github.com/vertcoin-project/one-click-miner-vnext/logging" "github.com/vertcoin-project/one-click-miner-vnext/networks" "github.com/vertcoin-project/one-click-miner-vnext/tracking" "github.com/vertcoin-project/one-click-miner-vnext/util" "github.com/vertcoin-project/one-click-miner-vnext/wallet" ) func (m *Backend) WalletInitialized() int { logging.Infof("Checking wallet..") checkWallet := 0 if keyfile.KeyFileValid() { checkWallet = 1 } script, err := keyfile.GetScript() if err != nil { logging.Errorf("Error initializing wallet: %s", err.Error()) } wal, err := wallet.NewWallet(keyfile.GetAddress(), script) if err != nil { logging.Errorf("Error initializing wallet: %s", err.Error()) } m.wal = wal logging.Infof("Wallet initialized: %d", checkWallet) return checkWallet } func (m *Backend) SendSweep(password string) []string { tracking.Track(tracking.TrackingRequest{ Category: "Sweep", Action: "Send", }) txids := make([]string, 0) if len(m.pendingSweep) == 0 { // Somehow user managed to press send without properly // preparing the sweep first return []string{"send_failed"} } for _, s := range m.pendingSweep { err := m.wal.SignMyInputs(s, password) if err != nil { logging.Errorf("Error signing transaction: %s", err.Error()) return []string{"sign_failed"} } txHash, err := m.wal.Send(s) if err != nil { logging.Errorf("Error sending transaction: %s", err.Error()) return []string{"send_failed"} } txids = append(txids, txHash) } m.pendingSweep = nil logging.Debugf("Transaction(s) sent! TXIDs: %v\n", txids) m.refreshBalanceChan <- true return txids } func (m *Backend) ShowTx(txid string) { util.OpenBrowser(fmt.Sprintf("%stx/%s", networks.Active.InsightURL, txid)) } type PrepareResult struct { FormattedAmount string NumberOfTransactions int } func (m *Backend) PrepareSweep(addr string) string { tracking.Track(tracking.TrackingRequest{ Category: "Sweep", Action: "Prepare", }) logging.Debugf("Preparing sweep") txs, err := m.wal.PrepareSweep(addr) if err != nil { logging.Errorf("Error preparing sweep: %v", err) return err.Error() } m.pendingSweep = txs val := float64(0) for _, tx := range txs { val += (float64(tx.TxOut[0].Value) / float64(100000000)) } result := PrepareResult{fmt.Sprintf("%0.8f VTC", val), len(txs)} logging.Debugf("Prepared sweep: %v", result) m.runtime.Events.Emit("createTransactionResult", result) return "" } func (m *Backend) Address() string { return keyfile.GetAddress() } func (m *Backend) InitWallet(password string) bool { tracking.Track(tracking.TrackingRequest{ Category: "Wallet", Action: "Initialize", }) err := keyfile.CreateKeyFile(password) if err == nil { m.WalletInitialized() m.ResetPool() return true } logging.Errorf("Error: %s", err.Error()) return false } ================================================ FILE: build.bat ================================================ @ECHO OFF IF NOT "%~1"=="" GOTO :BUILD :USAGE ECHO Usage: %~nx0 version GOTO :EOF :BUILD SET ver=%1 git describe --always --long --dirty > %TEMP%\git-version SET /p gitver=<%TEMP%\git-version DEL %TEMP%\git-version >nul 2>&1 CD tracking REN version.go version.go.build ECHO package tracking >> version.go ECHO var version="%ver%-%gitver%" >> version.go CD .. DEL build\vertcoin-ocm.exe >nul 2>&1 wails build ECHO "Sign the release assembly now on the windows machine if desired, then:" PAUSE CD build 7z -sdel -aou a vertcoin-ocm-%ver%-windows-x64.zip vertcoin-ocm.exe CD .. wails build -d ECHO "Sign the debug assembly now on the windows machine if desired, then:" PAUSE CD build 7z -sdel -aou a vertcoin-ocm-%ver%-windows-x64-debug.zip vertcoin-ocm.exe CD .. DEL *.syso *.manifest *.ico *.rc *.exe >nul 2>&1 CD tracking DEL version.go >nul 2>&1 REN version.go.build version.go CD .. go clean ================================================ FILE: build.sh ================================================ #!/bin/bash GITVER=$(git describe --always --long --dirty) mv tracking/version.go tracking/version.go.dev echo "package tracking" > tracking/version.go echo "var version=\"$1-$GITVER\"" >> tracking/version.go wails build cd build zip ../vertcoin-ocm-$1-linux-x64.zip ./vertcoin-ocm cd .. wails build -d cd build zip ../vertcoin-ocm-$1-linux-x64-debug.zip ./vertcoin-ocm cd .. rm tracking/version.go mv tracking/version.go.dev tracking/version.go ================================================ FILE: frontend/.gitignore ================================================ .DS_Store node_modules /dist # local env files .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw* ================================================ FILE: frontend/README.md ================================================ # vue basic ## Project setup ``` npm install ``` ### Compiles and hot-reloads for development ``` npm run serve ``` ### Compiles and minifies for production ``` npm run build ``` ### Run your tests ``` npm run test ``` ### Lints and fixes files ``` npm run lint ``` ### Customize configuration See [Configuration Reference](https://cli.vuejs.org/config/). ================================================ FILE: frontend/babel.config.js ================================================ module.exports = { presets: [ '@vue/app' ] } ================================================ FILE: frontend/package.json ================================================ { "name": "vertcoin_one_click_miner", "author": "Gert-Jaap Glasbergen", "private": true, "scripts": { "preinstall": "npx npm-force-resolutions", "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "@wailsapp/runtime": "^1.0.10", "core-js": "^2.6.11", "lodash": "^4.17.21", "lodash.clonedeep": "^4.5.0", "lodash.defaultsdeep": "^4.6.1", "vue": "^2.6.11", "vue-i18n": "^8.15.3" }, "devDependencies": { "@vue/cli-plugin-babel": "^4.5.15", "@vue/cli-plugin-eslint": "^3.12.1", "@vue/cli-service": "^4.5.15", "babel-eslint": "^10.0.3", "eslint": "^5.8.0", "eslint-plugin-vue": "^5.2.3", "eventsource-polyfill": "^0.9.6", "highlight.js": "^10.4.1", "vue-template-compiler": "^2.6.11", "webpack-hot-middleware": "^2.24.3" }, "eslintConfig": { "root": true, "env": { "node": true }, "extends": [ "plugin:vue/essential", "eslint:recommended" ], "rules": {}, "parserOptions": { "parser": "babel-eslint" } }, "postcss": { "plugins": { "autoprefixer": {} } }, "resolutions": { "minimist": "1.2.3", "mkdir": "0.5.3" }, "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ] } ================================================ FILE: frontend/package.json.md5 ================================================ 584b5614d6fc7dde9620d97ca74df594 ================================================ FILE: frontend/src/App.vue ================================================ ================================================ FILE: frontend/src/assets/css/main.css ================================================ #app { font-family: 'Montserrat', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #eee; -moz-user-select: none; -webkit-user-select: none; -ms-user-select:none; user-select:none; -o-user-select:none; margin: 0px; padding: 0px; font-size: 14px; } select { font-family: 'Montserrat', Helvetica, Arial, sans-serif; border: 2px solid #eee !important; background: #191b1c !important; color: #eee !important; -webkit-appearance: none; padding: 4px 10px; } /* ubuntu-regular - latin */ /* montserrat-regular - latin */ @font-face { font-family: 'Montserrat'; font-style: normal; font-weight: 400; src: url('../fonts/montserrat-v13-latin-regular.eot'); /* IE9 Compat Modes */ src: local('Montserrat Regular'), local('Montserrat-Regular'), url('../fonts/montserrat-v13-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('../fonts/montserrat-v13-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */ url('../fonts/montserrat-v13-latin-regular.woff') format('woff'), /* Modern Browsers */ url('../fonts/montserrat-v13-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */ url('../fonts/montserrat-v13-latin-regular.svg#Montserrat') format('svg'); /* Legacy iOS */ } html { height: 100%; overflow: hidden; background-color: #191b1c; background-size: 20px 20px; padding: 0px; margin: 0px; } body { padding: 0px; margin: 0px; } .logo { width: 16em; } .col-6 { width: 50%; float: left; } div.col-286 { margin: 0px; padding-left: 200px; padding-right: 200px; height: 100%; width: 100%; display: table-cell; vertical-align: middle; text-align: center; } div.col-settings { height: 235px; width: 80%; margin-left: 10%; vertical-align: middle; text-align: center; overflow-y: auto; display: flex; flex-basis: 1; } div.col-settings-sub { padding: 20px; width: 50%; flex-grow: 1; } div.col-286.height-100 { height: 100px; display: inline; } div.col-wide { margin: 0px; padding-left: 50px; padding-right: 50px; height: 100%; width: 100%; display: table-cell; vertical-align: middle; text-align: center; } div.settings-container { height: 348px; width: 100%; text-align: center; } div.container { height: 348px; width: 100%; display: table; text-align: center; } a.button:hover { opacity: 1.0; transition: 500ms; } a.button { opacity: 0.6; line-height: 45px; background: #048652; max-width: 286px; height: 45px; margin: 0 auto; display: block; color: white; z-index: 500; box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.15); cursor: pointer; font-weight: 400 !important; text-align: center; border-radius: 5px; } a.link, a.inlineLink { opacity: 0.6; color: #048652; cursor: pointer; margin: 0 auto; text-align: center; } a.link { display: block; } a.inlineLink { display: inline; } input[type='text'], input[type='password'] { border: 0px; background-color: #202225; color: white; border-radius: 5px; line-height: 45px; padding: 0px 10px; height: 45px; width: 400px; margin: 0 auto; font-family: 'Montserrat', Helvetica, Arial, sans-serif; font-size: 14px; } p.error { color: #900000; } input.error { border: 1px solid #900000; } input.success { border: 1px solid #048652; } input:-moz-focusring { color:transparent; text-shadow:0 0 0 #000; /* your normal text color here */ } input:-moz-focusring * { color:#000; /* your normal text color here */ text-shadow:none; } ================================================ FILE: frontend/src/components/Checks.vue ================================================ ================================================ FILE: frontend/src/components/Mining.vue ================================================ ================================================ FILE: frontend/src/components/Send.vue ================================================ ================================================ FILE: frontend/src/components/Settings.vue ================================================ ================================================ FILE: frontend/src/components/TabBar.vue ================================================ ================================================ FILE: frontend/src/components/Tracking.vue ================================================ ================================================ FILE: frontend/src/components/Update.vue ================================================ ================================================ FILE: frontend/src/components/Welcome.vue ================================================ ================================================ FILE: frontend/src/i18n/README.md ================================================ # Translation workflow If you want to add a new translation to the Vertcoin OCM you can follow two tracks: ## Create a pull-request (preferable) We expect you're familiar with how to create pull requests. If you're not, check [this article](https://akrabat.com/the-beginners-guide-to-contributing-to-a-github-project/) ### Step 1: Create a copy of the english base file Once in your local fork branch, make a copy of `en.json` in this directory (`frontend/src/i18n`), and rename it to match your desired language (for instance, for German you'd rename it to `de.json`). ### Step 2: Translate! Translate all the strings in the javascript file. Only translate the values not the identifiers, so: ```json { "generic" : { "retry" : "Retry", "back_to_wallet" : "Back to wallet" }, ... } ``` Would become ```json { "generic" : { "retry" : "Opnieuw proberen", "back_to_wallet" : "Terug naar portemonnee" }, ... } ``` **NOTE: Special characters** There are a couple of special characters that are not allowed in javascript string literals, including backslashes and double quotes. You need to escape them. But since they're not used at all in the English base text, it seems unlikely you'll need them. In case of doubt, you can escape them [here](https://www.freeformatter.com/json-escape.html) **NOTE: Character set** Please ensure the JSON file is saved using an UTF-8 character set. ### Step 3: Add language to frontend In the file `frontend/src/main.js` there's a list of the translations imported - add your new language there - ensure the list remains in alphabetical order: ```javascript // Import all locales import locale_de from "./i18n/de.json"; // <-- this line is added import locale_en from "./i18n/en.json"; import locale_nl from "./i18n/nl.json"; ``` Further down in the file, also add it to the list of languages injected to the i18n component - ensure the list remains in alphabetical order: ```javascript const i18n = new VueI18n({ locale: result, // set locale messages : { de: locale_de, // <-- this line is added en: locale_en, nl: locale_nl, }, }); ``` ### Step 4: Add language to the backend The host code running on the machine does the detection of the language and chooses the most appropriate one based on the user's locale. It needs to be made aware of the newly available language. Add this to the file `backend/languages.go` around line 9 - ensure the list remains in alphabetical order: ```golang var availableLanguages = []string{ "de", // <-- this line is added. Notice the comma on the end - it belongs there! "en", "nl", } ``` ### Step 5: Commit & Create PR You're done. Add all files to a commit, push it to your personal fork and then create a pull request to the main OCM repository. Thanks a ton for your contribution in advance! ## Alternative option (only if you fail at the above) The alternative option is that you just download the `en.js` file, translate it locally, and open an issue including the translated file. Then I can include it for you. But if you're able to use the PR workflow, I would really appreciate and prefer you use that! ================================================ FILE: frontend/src/i18n/bg.json ================================================ { "generic" : { "retry" : "Опитайте отново", "back_to_wallet" : "Обратно към портфейла", "close" : "Затвори" }, "welcome" : { "alreadyrunning" : "One-Click Miner вече работи. Не можете да го стартирате повече от веднъж.", "click_button_to_start" : "Щракнете върху бутона по-долу, за да започнете отново да копаете.", "startmining" : "Започнете да копаете!", "makeapassword" : "Създайте парола за One-Click Miner портфейла. Не я губете.", "password" : "Парола", "confirmpassword" : "Потвърдете паролата", "password_cannot_be_empty" : "Паролата не може да бъде празна", "password_mismatch" : "Паролите не съвпадат", "error_initializing" : "Нещо се обърка при инициализирането на портфейла" }, "checks" : { "prerequisite" : "Компонентите се инсталират. Може да видите изскачащ прозорец, който иска разрешения (може просто да мига в лентата на задачите)", "checks_failed" : "Проверките се провалиха", "checking_mining_software" : "Проверка на софтуера за копаене...", "rapidfail" : "Проверка за бързи провали", "compatibility" : "Проверка на съвместимостта на графичния процесор (GPU)...", "installing_miners" : "Инсталиране на софтуер за копаене...", "verthash" : "Проверка / създаване на Verthash файл с данни..." }, "mining" : { "spendable_balance" : "Достъпен баланс", "still_maturing" : "Очаква потвърждение", "pending_pool_payout" : "предстоящо изплащане на пула", "pending_payout_info_unavailable" : "Моля, изчакайте информация за предстоящо изплащане", "waiting_for_miners" : "Изчакайте стартирането на миньорите", "expected_earnings_24h" : "Очаквани доходи (24 часа)", "estimating" : "приблизително", "stop_mining" : "Спрете Копаенето", "active_pool" : "Активен басейн", "pool_fee" : "такса", "copy_address" : "Копирайте адреса на портфейла си", "payout_information" : "Преглед на информацията за изплащане" }, "sending" : { "send_all_to" : "Изпратете всичките си изкопани монети на", "youre_sending_x_to" : "Вие изпращате {receivedBalance} на", "youre_sending_x_in_y_txs_to" : "Вие изпращате {receivedBalance} в {receivedTxCount} транзакции към", "receiver_address" : "Адрес на получателя", "wallet_password" : "Парола на One-Click Miner портфейла", "send" : "Изпрати", "coins_sent" : "Вашите монети са изпратени!", "view_trans_plural" : "Преглед на транзакциите", "view_trans_singular" : "Преглед на транзакцията", "failed_to_send" : "Изпращането на вашите монети не бе успешно", "password_required" : "Изисква се парола за портфейла", "invalid_address" : "Невалиден адрес", "script_failure" : "Грешка в скрипта", "could_not_calculate_fee" : "Не може да се изчисли таксата", "insufficient_funds" : "Недостатъчни средства", "sign_failed" : "Транзакцията не може да бъде подписана. Проверете паролата.", "send_failed" : "Транзакцията не може да бъде изпратена. Проверете debug.log за повече информация" }, "settings" : { "enable_debug" : "Активиране на отстраняване на грешки", "enable_debug_sub" : "Включете изхода от миньорската конзола в дневника за отстраняване на грешки. Може значително да се увеличи размера на лог файла", "auto_start" : "Автоматично стартиране", "auto_start_sub" : "Стартиране на One-Click Miner когато влезете в компютъра си", "closed_source" : "Използвайте миньори със затворен код", "closed_source_sub" : "По-добър хешрейт, но неодитирани миньори, които може да носят такса за програмист", "closed_source_warning" : "Избрали сте да използвате миньори със затворен код. Vertcoin не одобрява или поддържа тези миньори. Те не могат да бъдат одитирани върху тяхното съдържание и могат да съдържат функции, които увреждат компютъра ви.", "enable_integrated" : "Копаене чрез вградената видеокарта", "enable_integrated_sub" : "Миньорът ще използва вградена видео карта (ако е възможно)", "testnet":"Режим тестова мрежа", "testnet_sub":"Активира режим на тестова мрежа. Няма да добивате истински монети.", "skipverthashverify":"Не проверявайте Verthash при стартиране.", "skipverthashverify_sub":"Ще пропусне проверката на целостта на Verthash файла при стартиране (не се препоръчва)", "save_n_restart" : "Запазване и Рестартиране", "pool": "Минен басейн" }, "tabbar" : { "wallet" : "Портфейл", "send_coins" : "Изпратете монети", "settings" : "Настройки" }, "tracking": { "update_available" : "Налична актуализация", "tracking_enabled" : "Вие анонимно споделяте статистически данни за използването на софтуера", "disable_tracking" : "Деактивиране", "tracking_disabled" : "Вие не споделяте статистически данни за използването на софтуера", "enable_tracking" : "Активирайте ги, за да ни помогнете да подобрим вашето преживяване", "report_issue" : "Подайте сигнал за проблем" }, "update" : { "new_version_available" : "Налична нова версия", "download" : "Изтегли" } } ================================================ FILE: frontend/src/i18n/da.json ================================================ { "generic" : { "retry" : "Prøv igen", "back_to_wallet" : "Tilbage til wallet", "close" : "Luk" }, "welcome" : { "alreadyrunning" : "One-Click Miner kører allerede. Du kan kun gøre en ad gangen.", "click_button_to_start" : "Tryk på knappen herunder for at starte med at mine igen.", "startmining" : "Start med at mine!", "makeapassword" : "Lav et password, glem det ikke!", "password" : "Password", "confirmpassword" : "Bekræft Password", "password_cannot_be_empty" : "Password kan ikke være tomt", "password_mismatch" : "De 2 Passwords er ikke ens", "error_initializing" : "Noget gik galdt under indlæsning af wallet" }, "checks" : { "prerequisite" : "Installation af en påkrævet pakke er igang. Du ser måske en popup der anmoder om tilladelser (måske som noget der blinker i din taskbar)", "checks_failed" : "Tjek fejlede", "checking_mining_software" : "Kontrollerer mining software...", "rapidfail" : "Kontrollerer for hurtigt opstående fejl", "compatibility" : "Kontrollerer for GPU kompatibilitet...", "installing_miners" : "Installerer mine software...", "verthash" : "Verificerer / generere Verthash data fil..." }, "mining" : { "spendable_balance" : "Brugbar Balance", "still_maturing" : "modner stadig", "pending_pool_payout" : "afventende pooludbetaling", "pending_payout_info_unavailable" : "Vent venligst for pool udbetalingsoplysninger", "waiting_for_miners" : "Venter på at minerne starter", "expected_earnings_24h" : "Forventet indtjening (24 timer)", "estimating" : "estimerer", "stop_mining" : "Stop med at mine", "active_pool" : "Aktiv pool", "pool_fee" : "gebyr", "copy_address" : "Kopier din wallet adresse", "payout_information" : "Se udbetalingsoplysninger" }, "sending" : { "send_all_to" : "Send alle dine minede coins til", "youre_sending_x_to" : "Du sender {receivedBalance} til", "youre_sending_x_in_y_txs_to" : "Du sender {receivedBalance} igennem {receivedTxCount} transaktioner til", "receiver_address" : "Modtager Adresse", "wallet_password" : "OCM Wallet Password", "send" : "Send", "coins_sent" : "Dine coins er blevet sendt!", "view_trans_plural" : "Se transaktioner", "view_trans_singular" : "Se transaktion", "failed_to_send" : "Kunne ikke sende dine coins", "password_required" : "Wallet password er påkrævet", "invalid_address" : "Ugyldig adresse", "script_failure" : "Script fejl", "could_not_calculate_fee" : "Kunne ikke beregne gebyr", "insufficient_funds" : "Utilstrækkelige midler", "sign_failed" : "Kunne ikke underskrive transaktion. Tjek dit password.", "send_failed" : "Kunne ikke sende transaktion. Check debug.log for mere information" }, "settings" : { "enable_debug" : "Aktiver debugging", "enable_debug_sub" : "Inkludér minerens consol output i debug log. Kan gøre logfilerne temmeligt store", "auto_start" : "Auto start", "auto_start_sub" : "Start One-Click Miner når du logger på din computer", "closed_source" : "Brug closed-source minere", "closed_source_sub" : "Bedre hashrate, men uanmeldte minere der pådrager sig udvikler gebyr", "closed_source_warning" : "Du har valgt at benytte closed-source miner(e). Vertcoin støtter og supporterer IKKE disse minere. De kan ikke undersøges for indhold og kunne derfor benytte sig af funktioner der kan være skadelige for din computer.", "enable_integrated" : "Brug indbygget grafik til at mine", "enable_integrated_sub" : "Indbygget grafik vil blive brugt af mineren (hvis det er muligt)", "testnet": "Testnet mode", "testnet_sub": "Aktiver testnet. Du vil ikke mine rigtige coins.", "skipverthashverify": "Stop med at verificere Verthash data fil under opstart", "skipverthashverify_sub": "Spring verifikationen af verthash data filen over (ikke anbefalet)", "save_n_restart" : "Gem & Genstart", "pool": "Mining pool" }, "tabbar" : { "wallet" : "Wallet", "send_coins" : "Send coins", "settings" : "Indstillinger" }, "tracking": { "update_available" : "Opdatering tilgængelig", "tracking_enabled" : "Du deler brugsstatistikker anonymt", "disable_tracking" : "Slå deling af statistik fra", "tracking_disabled" : "Du deler ikke brugsstatistikker", "enable_tracking" : "Slå deling af statistik til for at hjælpe os med at forbedre din oplevelse", "report_issue" : "Anmeld fejl eller problemer" }, "update" : { "new_version_available" : "Ny version er tilgængelig", "download" : "Download" } } ================================================ FILE: frontend/src/i18n/de.json ================================================ { "generic" : { "retry" : "Wiederholen", "back_to_wallet" : "Zurück zur Wallet", "close" : "Schließen" }, "welcome" : { "alreadyrunning" : "One-Click-Miner wird bereits ausgeführt. Sie können ihn nicht mehr als einmal ausführen.", "click_button_to_start" : "Klicken Sie auf die Schaltfläche unten, um das Mining erneut zu starten.", "startmining" : "Mining starten!", "makeapassword" : "Erstellen Sie ein Passwort. Verlieren Sie es nicht!", "password" : "Passwort", "confirmpassword" : "Passwort bestätigen", "password_cannot_be_empty" : "Passwort darf nicht leer sein", "password_mismatch" : "Passwörter stimmen nicht überein", "error_initializing" : "Fehler beim Initialisieren der Wallet" }, "checks" : { "prerequisite" : "Voraussetzungen werden installiert. Möglicherweise wird ein Fenster angezeigt, in dem Sie nach Berechtigungen gefragt werden (möglicherweise blinkt es in der Taskleiste)", "checks_failed" : "Überprüfung fehlgeschlagen", "checking_mining_software" : "Mining-Software wird überprüft...", "rapidfail" : "Auftreten von schnellen Ausfällen wird geprüft", "compatibility" : "GPU-Kompatibilität wird überprüft...", "installing_miners" : "Mining-Software wird installiert...", "verthash" : "Verthash Daten werden angelegt/verifiziert..." }, "mining" : { "spendable_balance" : "Verfügbares Guthaben", "still_maturing" : "reift noch", "pending_pool_payout" : "ausstehende Pool-Auszahlung", "pending_payout_info_unavailable": "Ausstehende Pool-Auszahlung wird abgerufen", "waiting_for_miners" : "Warten auf Start des Miners", "expected_earnings_24h" : "Voraussichtliche Einnahmen (24h)", "estimating" : "am schätzen", "stop_mining" : "Mining stoppen", "active_pool" : "Aktiver Pool", "pool_fee" : "Pool-Gebühr", "copy_address" : "Wallet-Adresse kopieren", "payout_information" : "Auszahlungsinformationen ansehen" }, "sending" : { "send_all_to" : "Senden Sie all Ihre Coins an", "youre_sending_x_to" : "Sie senden {receivedBalance} Coins an", "youre_sending_x_in_y_txs_to" : "Sie senden {receivedBalance} Coins in {receivedTxCount} Transaktionen an", "receiver_address" : "Empfängeradresse", "wallet_password" : "Wallet-Passwort", "send" : "Senden", "coins_sent" : "Ihre Coins wurden versendet!", "view_trans_plural" : "Transaktionen anzeigen", "view_trans_singular" : "Transaktion anzeigen", "failed_to_send" : "Fehler beim Senden Ihrer Coins", "password_required" : "Das Wallet-Passwort wird benötigt", "invalid_address" : "Ungültige Adresse", "script_failure" : "Skriptfehler", "could_not_calculate_fee" : "Gebührenkalkulation fehlgeschlagen", "insufficient_funds" : "unzureichende Mittel", "sign_failed" : "Transaktion kann nicht signiert werden. Überprüfen Sie Ihr Passwort", "send_failed" : "Transaktion fehlgeschlagen. Weitere Informationen finden Sie in debug.log" }, "settings" : { "enable_debug" : "Debuggen aktivieren", "enable_debug_sub" : "Konsolenausgabe der Mining-Software in das Debugprotokoll mit aufnehmen. Kann die Größe Ihrer Protokolle erheblich erhöhen", "auto_start" : "Auto-Start", "auto_start_sub" : "One-Click-Miner bei der Anmeldung starten", "closed_source" : "Proprietäre Mining-Software nutzen", "closed_source_sub" : "Bessere Hash-Rate, aber ungeprüfte Miner mit Entwicklergebühr.", "closed_source_warning" : "Sie haben sich für die Verwendung proprietärer Mining-Software entschieden. Vertcoin bietet keine Unterstützung an. Die Miner können nicht überprüft werden und enthalten möglicherweise Funktionen, die Ihren Computer beeinträchtigen.", "enable_integrated" : "Auf integrierter Grafikkarte minen", "enable_integrated_sub" : "Miner wird die Onboard-Grafikkarte verwenden (wenn möglich)", "testnet" : "Testnet-Modus", "testnet_sub" : "Aktiviert Testnet-Modus. Sie werden keine echten Coins minen.", "skipverthashverify" : "Verthash-Überprüfung beim Start überspringen", "skipverthashverify_sub" : "Die Prüfung der Datenintegrität der Verthash-Daten beim Start wird übersprungen (nicht empfohlen)", "save_n_restart" : "Speichern & Neustarten", "pool" : "Mining-Pool" }, "tabbar" : { "wallet" : "Wallet", "send_coins" : "Coins senden", "settings" : "Einstellungen" }, "tracking": { "update_available" : "Update verfügbar", "tracking_enabled" : "Nutzungsstatistiken werden anonym geteilt", "disable_tracking" : "Deaktivieren", "tracking_disabled" : "Sie teilen keine Nutzungsstatistiken", "enable_tracking" : "Aktivieren Sie diese Option, um uns bei der Verbesserung der Benutzerfreundlichkeit zu helfen", "report_issue" : "Ein Problem melden" }, "update" : { "new_version_available" : "Neue Version verfügbar", "download" : "Herunterladen" } } ================================================ FILE: frontend/src/i18n/en.json ================================================ { "generic" : { "retry" : "Retry", "back_to_wallet" : "Back to wallet", "close" : "Close" }, "welcome" : { "alreadyrunning" : "The One-Click Miner is already running. You can't run it more than once.", "click_button_to_start" : "Click the button below to start mining again.", "startmining" : "Start Mining!", "makeapassword" : "Make an OCM Wallet password. Don't lose it.", "password" : "Password", "confirmpassword" : "Confirm Password", "password_cannot_be_empty" : "Password cannot be empty", "password_mismatch" : "Passwords do not match", "error_initializing" : "Something went wrong initializing the wallet" }, "checks" : { "prerequisite" : "A prerequisite is being installed. You might see a popup asking for permissions (could just be blinking in the taskbar)", "checks_failed" : "Checks failed", "checking_mining_software" : "Checking mining software...", "rapidfail" : "Checking for rapid failure occurrences", "compatibility" : "Checking GPU compatibility...", "installing_miners" : "Installing mining software...", "verthash" : "Verifying / creating Verthash data file..." }, "mining" : { "spendable_balance" : "Spendable Balance", "still_maturing" : "still maturing", "pending_pool_payout" : "pending pool payout", "pending_payout_info_unavailable" : "Please wait for pending payout information", "waiting_for_miners" : "Waiting for miners to start", "expected_earnings_24h" : "Expected Earnings (24h)", "estimating" : "estimating", "stop_mining" : "Stop Mining", "active_pool" : "Active pool", "pool_fee" : "fee", "copy_address" : "Copy your wallet address", "payout_information" : "View payout information" }, "sending" : { "send_all_to" : "Send all your mined coins to", "youre_sending_x_to" : "You're sending {receivedBalance} to", "youre_sending_x_in_y_txs_to" : "You're sending {receivedBalance} in {receivedTxCount} transactions to", "receiver_address" : "Receiver Address", "wallet_password" : "OCM Wallet Password", "send" : "Send", "coins_sent" : "Your coins are sent!", "view_trans_plural" : "View transactions", "view_trans_singular" : "View transaction", "failed_to_send" : "Failed to send your coins", "password_required" : "Wallet password is required", "invalid_address" : "Invalid address", "script_failure" : "Script failure", "could_not_calculate_fee" : "Could not calculate fee", "insufficient_funds" : "Insufficient funds", "sign_failed" : "Unable to sign transaction. Check password.", "send_failed" : "Unable to send transaction. Check debug.log for more information" }, "settings" : { "enable_debug" : "Enable debugging", "enable_debug_sub" : "Include the miner's console output in the debug log. Can grow your logs quite large", "auto_start" : "Auto start", "auto_start_sub" : "Start the One-Click Miner when you log in your computer", "closed_source" : "Use closed-source miners", "closed_source_sub" : "Better hashrate, but unaudited miners that incur a developer's fee", "closed_source_warning" : "You have selected to use closed source miner(s). Vertcoin does not endorse or support these miners. They cannot be audited on their contents and could contain functions that harm your computer.", "enable_integrated" : "Mine on integrated graphics", "enable_integrated_sub" : "The miner will use onboard graphics (if possible)", "testnet":"Testnet mode", "testnet_sub":"Enables testnet mode. You will not be mining real coins.", "skipverthashverify":"Don't verify Verthash on startup", "skipverthashverify_sub":"Will skip verifying the file integrity of Verthash data on startup (not recommended)", "save_n_restart" : "Save & Restart", "pool": "Mining pool" }, "tabbar" : { "wallet" : "Wallet", "send_coins" : "Send coins", "settings" : "Settings" }, "tracking": { "update_available" : "Update available", "tracking_enabled" : "You are anonymously sharing usage statistics", "disable_tracking" : "Disable", "tracking_disabled" : "You are not sharing usage statistics", "enable_tracking" : "Enable these to help us improve your experience", "report_issue" : "Report an issue" }, "update" : { "new_version_available" : "New version available", "download" : "Download" } } ================================================ FILE: frontend/src/i18n/es.json ================================================ { "generic" : { "retry" : "Reintentar", "back_to_wallet" : "Volver a la billetera", "close" : "Cerrar" }, "welcome" : { "alreadyrunning" : "El minero de un click ya se está ejecutando. Solo se permite una instancia del programa.", "click_button_to_start" : "Haz click en el botón para empezar a minar de nuevo.", "startmining" : "¡Empezar a minar!", "makeapassword" : "Crea una contraseña para la billetera del minero de un click. No la pierdas.", "password" : "Contraseña.", "confirmpassword" : "Confirmar contraseña.", "password_cannot_be_empty" : "La contraseña no puede estar vacía.", "password_mismatch" : "Las contraseñas no coinciden.", "error_initializing" : "Hubo un problema al iniciar la billetera." }, "checks" : { "prerequisite" : "Un prerequisito está siendo instalado. Puede ser que veas una ventana emergente pidiendo permisos (podría estar parpadeando en la barra de tareas.)", "checks_failed" : "Chequeos fallados", "checking_mining_software" : "Chequeando el software de minería...", "rapidfail" : "Comprobación de fallos rápidos", "compatibility" : "Chequeando compatibilidad de la tarjeta gráfica...", "installing_miners" : "Instalando software de minería...", "verthash" : "Verificando / creando archivo de datos de Verthash." }, "mining" : { "spendable_balance" : "Saldo disponible", "still_maturing" : "Todavía no puedes gastar tus monedas, se necesitan más confirmaciones de la red.", "pending_pool_payout" : "Pago de la pool pendiente.", "pending_payout_info_unavailable" : "Información de pago no disponible en este momento.", "waiting_for_miners" : "Esperando a que los mineros empiecen.", "expected_earnings_24h" : "Ganancias esperadas (24h).", "estimating" : "Estimando", "stop_mining" : "Dejar de minar.", "active_pool" : "Pool activa.", "pool_fee" : "Comisión.", "copy_address" : "Copia la dirección de tu billetera.", "payout_information" : "Mostrar la información del pago." }, "sending" : { "send_all_to" : "Envia todas tus monedas minadas a", "youre_sending_x_to" : "Estás enviando {receivedBalance} a", "youre_sending_x_in_y_txs_to" : "Estás enviando {receivedBalance} en {receivedTxCount} transacciones a", "receiver_address" : "Dirección del receptor", "wallet_password" : "Contraseña de la billetera del minero de un click", "send" : "Enviar", "coins_sent" : "¡Tus monedas han sido enviadas!", "view_trans_plural" : "Mostrar transacciones", "view_trans_singular" : "Mostrar transacción", "failed_to_send" : "Hubo un fallo al enviar tus monedas", "password_required" : "La contraseña de la billetera es requerida", "invalid_address" : "Dirección inválida", "script_failure" : "Fallo de código", "could_not_calculate_fee" : "No se pudo calcular la comisión", "insufficient_funds" : "Fondos insuficientes", "sign_failed" : "No se pudo firmar la transacción. Chequea la contraseña", "send_failed" : "No se pudo enviar la transacción. Mira debug.log para más información" }, "settings" : { "enable_debug" : "Activar información de depuración", "enable_debug_sub" : "Incluye las salidas del minero en la consola en el registro de depuración. Puede hacer que tus registros sean bastante grandes", "auto_start" : "Empezar automáticamente", "auto_start_sub" : "Empezar el minero de un click cuando inicias sesión en tu ordenador", "closed_source" : "Usa mineros de código cerrado", "closed_source_sub" : "Mejor rendimiento, pero mineros que no pueden ser inspeccionados y que sufren de una comisión de desarollo", "closed_source_warning" : "Has seleccionado usar minero(s) de código cerrado. Vertcoin no respalda ni ayuda a estos mineros. Sus contenidos no pueden ser inspeccionados y podrían contener funciones que podrían dañar tu ordenador.", "enable_integrated" : "Minar en gráficos integrados", "enable_integrated_sub" : "El minero utilizará gráficos integrados (si es posible)", "testnet":"Modo Testnet", "testnet_sub":"Activa el modo Tesnet. No estarás minando monedas reales.", "skipverthashverify":"No verificar Verthash al encender", "skipverthashverify_sub":"Se saltará la verificación de la integridad de la información de Verthash al encender (no recomendado)", "save_n_restart" : "Guardar y reiniciar", "pool": "Pool de minería" }, "tabbar" : { "wallet" : "billetera", "send_coins" : "Enviar monedas", "settings" : "Opciones" }, "tracking": { "update_available" : "Actualización disponible", "tracking_enabled" : "Estás compartiendo datos de uso anónimamente", "disable_tracking" : "Desactivar", "tracking_disabled" : "No estás compartiendo datos de uso", "enable_tracking" : "Activar el uso de datos compartidos para ayudarnos a mejorar tu experiencia", "report_issue" : "Reportar un problema" }, "update" : { "new_version_available" : "Nueva versión disponible", "download" : "Descargar" } } ================================================ FILE: frontend/src/i18n/fr.json ================================================ { "generic" : { "retry" : "Réessayer", "back_to_wallet" : "Revenir au portefeuille", "close" : "Fermer" }, "welcome" : { "alreadyrunning" : "One-Click Miner est déjà en cours d'exécution. Vous ne pouvez pas l'exécuter plus d'une fois.", "click_button_to_start" : "Cliquer sur le bouton ci-dessous pour redémarrer le minage.", "startmining" : "Démarrer le minage !", "makeapassword" : "Créer un mot de passe. Ne pas le perdre !", "password" : "Mot de passe", "confirmpassword" : "Confirmer le mot de passe", "password_cannot_be_empty" : "Le mot de passe ne peut être vide", "password_mismatch" : "Le mot de passe ne correspond pas", "error_initializing" : "Erreur pendant l'initialisation du portefeuille" }, "checks" : { "prerequisite" : "Un prérequis est en cours d'installation. Vous pourriez voir une fenêtre demandant des autorisations (elle pourrait simplement clignoter dans la barre des tâches)", "checks_failed" : "Les vérifications ont echoué", "checking_mining_software" : "Vérification du logiciel de minage...", "rapidfail" : "Vérification des occurrences d'échec rapide", "compatibility" : "Vérification de la compatibilité GPU...", "installing_miners" : "Installation du logiciel de minage..." }, "mining" : { "spendable_balance" : "Solde disponible", "still_maturing" : "En cours de consolidation", "pending_pool_payout" : "Paiement de la pool en attente", "waiting_for_miners" : "En attente de mineurs pour démarrer", "expected_earnings_24h" : "Bénéfices attendus (24h)", "estimating" : "Estimation", "stop_mining" : "Arrêt du minage" }, "sending" : { "send_all_to" : "Envoyer toute votre cryptomonnaie minée à", "youre_sending_x_to" : "Vous envoyez {receivedBalance} à", "youre_sending_x_in_y_txs_to" : "Vous envoyez {receivedBalance} en {receivedTxCount} transactions à", "receiver_address" : "Adresse du destinataire", "wallet_password" : "Mot de passe du portefeuille", "send" : "Envoyer", "coins_sent" : "Votre cryptomonnaie a été envoyée !", "view_trans_plural" : "Voir les transactions", "view_trans_singular" : "Voir la transaction", "failed_to_send" : "Echec lors de l'envoi de votre cryptomonnaie", "password_required" : "Le mot de passe du portefeuille est requis", "invalid_address" : "Adresse invalide", "script_failure" : "Script défectueux", "could_not_calculate_fee" : "Calcul des frais impossible", "insufficient_funds" : "Fonds insuffisants", "sign_failed" : "Impossible de signer la transaction. Vérifiez votre mot de passe", "send_failed" : "Transaction impossible. Vérifier debug.log pour plus d'informations" }, "settings" : { "enable_debug" : "Activer le débogage", "enable_debug_sub" : "Inclure la sortie console du logiciel de minage dans le log de débogage. Peut augmenter considérablement la taille de vos logs", "auto_start" : "Démarrage auto", "auto_start_sub" : "Démarrer One-Click Miner quand vous vous connectez à votre ordinateur", "closed_source" : "Utiliser des logiciels de minage propriétaires", "closed_source_sub" : "Meilleur taux de hachage, mais logiciel de minage ne rétribuant pas les développeurs", "closed_source_warning" : "Vous avez choisi d'utiliser des logiciels de minage propriétaires. Vertcoin n'en assure pas le support. Leur contenu ne peut pas être audité et pourrait comporter des fonctions qui endommagent votre ordinateur.", "save_n_restart" : "Enregistrer & Redémarrer" }, "tabbar" : { "wallet" : "Portefeuille", "send_coins" : "Envoyer de la cryptomonnaie", "settings" : "Paramètres" }, "tracking": { "update_available" : "Mise à jour disponible", "tracking_enabled" : "Vous partagez anonymement vos statistiques d'utilisation", "disable_tracking" : "Désactiver", "tracking_disabled" : "Vous ne partagez pas vos statistiques d'utilisation", "enable_tracking" : "Activer cette option pour nous permettre d'améliorer votre expérience utilisateur", "report_issue" : "Signaler un problème" }, "update" : { "new_version_available" : "Nouvelle version disponible", "download" : "Télécharger" } } ================================================ FILE: frontend/src/i18n/hi.json ================================================ { "generic" : { "retry" : "पुन: प्रयास", "back_to_wallet" : "वापस वॉलेट में", "close" : "बंद करे" }, "welcome" : { "alreadyrunning" : "वन-क्लिक माइनर पहले से ही चल रहा है। आप इसे एक से अधिक बार नहीं चला सकते।", "click_button_to_start" : "फिर से खनन शुरू करने के लिए नीचे दिए गए बटन पर क्लिक करें।", "startmining" : "खनन शुरू करो!", "makeapassword" : "एक पासवर्ड बनाएं। इसे खोना नहीं है।", "password" : "पारण शब्द", "confirmpassword" : "पासवर्ड की पुष्टि कीजिये", "password_cannot_be_empty" : "पासवर्ड खाली नहीं हो सकता", "password_mismatch" : "पासवर्ड मेल नहीं खाते", "error_initializing" : "वॉलेट को शुरू करने में कुछ गलत हुआ" }, "checks" : { "prerequisite" : "एक शर्त लगाई जा रही है। आप एक पॉपअप अनुमति के लिए पूछ सकते हैं (बस टास्कबार में निमिष हो सकता है)", "checks_failed" : "जाँच विफल", "checking_mining_software" : "खनन सॉफ्टवेयर की जाँच करना...", "rapidfail" : "तेजी से विफलता की घटनाओं के लिए जाँच", "compatibility" : "ग्राफिक्स प्रोसेसिंग यूनिट संगतता की जाँच करना...", "installing_miners" : "खनन सॉफ्टवेयर स्थापित करना..." }, "mining" : { "spendable_balance" : "व्यय करने योग्य शेष", "still_maturing" : "अभी भी परिपक्व हो रहा है", "pending_pool_payout" : "लंबित पूल भुगतान", "waiting_for_miners" : "खनिकों के शुरू होने का इंतजार", "expected_earnings_24h" : "अपेक्षित कमाई (चौबीस घंटे)", "estimating" : "का आकलन", "stop_mining" : "खनन बंद करो" }, "sending" : { "send_all_to" : "करने के लिए अपने सभी खनन सिक्के भेजें", "youre_sending_x_to" : "आप भेज रहे हैं {receivedBalance} सेवा मेरे", "youre_sending_x_in_y_txs_to" : "आप भेज रहे हैं {receivedBalance} में {receivedTxCount} लेन-देन", "receiver_address" : "रिसीवर का पता", "wallet_password" : "वॉलेट पासवर्ड", "send" : "भेजना", "coins_sent" : "आपके सिक्के भेजे हैं", "view_trans_plural" : "लेन-देन देखें", "view_trans_singular" : "लेन-देन देखें", "failed_to_send" : "अपने सिक्के भेजने में विफल", "password_required" : "वॉलेट पासवर्ड की आवश्यकता है", "invalid_address" : "गलत पता", "script_failure" : "स्क्रिप्ट की विफलता", "could_not_calculate_fee" : "शुल्क की गणना नहीं कर सका", "insufficient_funds" : "अपर्याप्त कोष", "sign_failed" : "लेन-देन पर हस्ताक्षर करने में असमर्थ। अपना पासवर्ड जांचें", "send_failed" : "लेनदेन भेजने में असमर्थ। अधिक जानकारी के लिए debug.log की जाँच करें" }, "settings" : { "enable_debug" : "डिबगिंग सक्षम करें", "enable_debug_sub" : "डिबग लॉग में माइनर के कंसोल आउटपुट को शामिल करें। अपने लॉग को काफी बड़ा कर सकते हैं", "auto_start" : "ऑटो स्टार्ट", "auto_start_sub" : "जब आप अपने कंप्यूटर में लॉग इन करते हैं तो वन-क्लिक माइनर शुरू करें", "closed_source" : "बंद-स्रोत खनिक का उपयोग करें", "closed_source_sub" : "बेहतर हैशेट, लेकिन अघोषित खनिक जो एक डेवलपर के शुल्क को बढ़ाते हैं", "closed_source_warning" : "आपने बंद स्रोत खननकर्ता का उपयोग करने के लिए चुना है। Vertcoin इन खनिकों का समर्थन या समर्थन नहीं करता है। उनकी सामग्री पर उनका ऑडिट नहीं किया जा सकता है और इसमें आपके कंप्यूटर को नुकसान पहुंचाने वाले कार्य हो सकते हैं।", "save_n_restart" : "सहेजें और पुनरारंभ करें" }, "tabbar" : { "wallet" : "बटुआ", "send_coins" : "सिक्के भेजें", "settings" : "सेटिंग्स" }, "tracking": { "update_available" : "अपडेट उपलब्ध", "tracking_enabled" : "आप गुमनाम रूप से उपयोग के आंकड़े साझा कर रहे हैं", "disable_tracking" : "अक्षम", "tracking_disabled" : "आप उपयोग के आंकड़े साझा नहीं कर रहे हैं", "enable_tracking" : "अपने अनुभव को बेहतर बनाने में हमारी सहायता करने के लिए इन्हें सक्षम करें", "report_issue" : "मामले की रिपोर्ट करें" }, "update" : { "new_version_available" : "नया संस्करण उपलब्ध है", "download" : "डाउनलोड" } } ================================================ FILE: frontend/src/i18n/hr.json ================================================ { "generic" : { "retry" : "Pokušajte ponovno", "back_to_wallet" : "Natrag na novčanik", "close" : "Zatvoriti" }, "welcome" : { "alreadyrunning" : "One-Click Miner već radi. Ne možete ga pokrenuti više od jednom.", "click_button_to_start" : "Pritisnite dugme ispod da ponovno započnete sa rudarenjem.", "startmining" : "Započnite sa rudarenjem!", "makeapassword" : "Kreirajte lozinku. Nemojte ju izgubiti.", "password" : "Lozinka", "confirmpassword" : "Potvrdite lozinku", "password_cannot_be_empty" : "Polje za lozinku ne može biti prazno", "password_mismatch" : "Lozinke se ne podudaraju", "error_initializing" : "Pogreška kod učitavanja novčanika" }, "checks" : { "prerequisite" : "Skidanje preduvjetnih datoteka. Mogućnost iskakanja skočnog prozora za dopuštenja (može treperiti u alatnoj traci)", "checks_failed" : "Provjera neuspješna", "checking_mining_software" : "Provjera softvera za rudarenje...", "rapidfail" : "Provjera učestalosti neuspješnih radnji", "compatibility" : "Provjera kompatibilnosti grafičke kartice...", "installing_miners" : "Instalacija softvera za rudarenje..." }, "mining" : { "spendable_balance" : "Dostupan iznos", "still_maturing" : "nedozrelo", "pending_pool_payout" : "očekivana pool isplata", "waiting_for_miners" : "Čekanje da softver za rudarenje započne sa radom", "expected_earnings_24h" : "Očekivana zarada(24h)", "estimating" : "Vršim procjenu", "stop_mining" : "Zaustavi rudarenje" }, "sending" : { "send_all_to" : "Pošalji sve izrudarene novčiće na", "youre_sending_x_to" : "Šaljete {receivedBalance} na", "youre_sending_x_in_y_txs_to" : "Šaljete {receivedBalance} u {receivedTxCount} transakcija na", "receiver_address" : "Adresa primatelja", "wallet_password" : "Lozinka novčanika", "send" : "Pošalji", "coins_sent" : "Vaši novčići su poslani!", "view_trans_plural" : "Vidi transakciju", "view_trans_singular" : "Vidi transakcije", "failed_to_send" : "Neuspješno slanje vaših novčića", "password_required" : "Potrebna je lozinka novčanika", "invalid_address" : "Nepostojeća adresa", "script_failure" : "Greška skripte", "could_not_calculate_fee" : "Nije moguće izračunati naknadu", "insufficient_funds" : "Nedostatna sredstva", "sign_failed" : "Nije moguće potpisati transakciju. Provjerite zaporku", "send_failed" : "Nije moguće poslati transakciju. Provjerite debug.log za više informacija" }, "settings" : { "enable_debug" : "Omogući otklanjanje bugova", "enable_debug_sub" : "Omogući izvoz logova konzole softvera za rudarenje u log za otklanjanje bugova. Veličina loga može se znatno povećati", "auto_start" : "Automatski početak", "auto_start_sub" : "Pokreni One-Click Miner kada se logiraš na računalo", "closed_source" : "Koristi softver za rudarenje zatvorenog koda", "closed_source_sub" : "Bolji hashrate, ali neocijenjeni softver za rudarenje može uključivati naknadu za razvojne inženjere", "closed_source_warning" : "Odlučili ste koristiti softver(e) za rudarenje zatvorenog koda. Vertcoin ne odobrava i ne podržava njihovu upotrebu. Ne može se provjeriti njihov sadržaj i potencijalno mogu sadržavati funkcije koje mogu oštetiti vaše računalo.", "save_n_restart" : "Spremi i ponovno pokreni" }, "tabbar" : { "wallet" : "Novčanik", "send_coins" : "Pošalji novčiće", "settings" : "Postavke" }, "tracking": { "update_available" : "Nova inačica je dostupna", "tracking_enabled" : "Anonimno dijelite vašu statistiku o korištenju", "disable_tracking" : "Onemogući", "tracking_disabled" : "Ne dijelite statistiku o korištenju", "enable_tracking" : "Omogućite i pomognite nam da unaprijedimo korisničko iskustvo", "report_issue" : "Prijavite problem" }, "update" : { "new_version_available" : "Nova verzija je dostupna", "download" : "Skidanje" } } ================================================ FILE: frontend/src/i18n/it.json ================================================ { "generic" : { "retry" : "Riprova", "back_to_wallet" : "Torna al portafoglio", "close" : "Chiudi" }, "welcome" : { "alreadyrunning" : "One-Click Miner è già in esecuzione. Non puoi eseguirlo più di una volta.", "click_button_to_start" : "Fai clic sul pulsante sottostante per ricominciare a minare.", "startmining" : "Inizia a Minare!", "makeapassword" : "Crea una password per il portafoglio OCM. Non perderla.", "password" : "Password", "confirmpassword" : "Conferma Password", "password_cannot_be_empty" : "La password non può essere vuota", "password_mismatch" : "Le password non corrispondono", "error_initializing" : "Qualcosa è andato storto nell'inizializzazione del portafoglio" }, "checks" : { "prerequisite" : "Un prerequisito sta venendo installato. Potresti vedere un popup che richiede dei permessi (potrebbe anche solo lampeggiare nella barra delle applicazioni)", "checks_failed" : "Controlli falliti", "checking_mining_software" : "Controllo del software di mining...", "rapidfail" : "Controllo di occorrenze di fallimento rapido", "compatibility" : "Controllo di compatibilità della GPU...", "installing_miners" : "Installazione del software di mining...", "verthash" : "Verifica / creazione del file di dati Verthash..." }, "mining" : { "spendable_balance" : "Saldo Disponibile", "still_maturing" : "non ancora maturato", "pending_pool_payout" : "pagamento del pool in sospeso", "pending_payout_info_unavailable" : "Perfavore attendi le informazioni sul pagamento in sospeso", "waiting_for_miners" : "In attesa che i miner si avviino", "expected_earnings_24h" : "Guadagno Previsto (24h)", "estimating" : "calcolo in corso", "stop_mining" : "Smetti di Minare", "active_pool" : "Pool attiva", "pool_fee" : "tassa", "copy_address" : "Copia l'indirizzo del tuo portafoglio", "payout_information" : "Visualizza le informazioni di pagamento" }, "sending" : { "send_all_to" : "Invia tutte le tue monete minate a", "youre_sending_x_to" : "Stai inviando {receivedBalance} a", "youre_sending_x_in_y_txs_to" : "Stai inviando {receivedBalance} in {receivedTxCount} transazioni a", "receiver_address" : "Indirizzo del Destinatario", "wallet_password" : "Password del Portafoglio OCM", "send" : "Invia", "coins_sent" : "Le tue monete sono state inviate!", "view_trans_plural" : "Visualizza le transazioni", "view_trans_singular" : "Visualizza la transazione", "failed_to_send" : "Fallito l'invio delle tue monete", "password_required" : "La password del portafoglio è obbligatoria", "invalid_address" : "Indirizzo non valido", "script_failure" : "Fallimento nello script", "could_not_calculate_fee" : "Impossibile calcolare la tassa", "insufficient_funds" : "Fondi insufficienti", "sign_failed" : "Non è stato possibile firmare la transazione. Controlla la password", "send_failed" : "Non è stato possibile inviare la transazione. Controlla debug.log per ulteriori informazioni" }, "settings" : { "enable_debug" : "Abilita il debug", "enable_debug_sub" : "Includi l'output della console del miner nel log di debug. Può far aumentare di molto la dimensione dei tuoi log", "auto_start" : "Avvio automatico", "auto_start_sub" : "Avvia One-Click Miner quando esegui l'accesso al tuo computer", "closed_source" : "Usa miner closed-source", "closed_source_sub" : "Miglior hashrate, ma miner non certificati che incorrono in una tassa per gli sviluppatori", "closed_source_warning" : "Hai scelto di usare uno o più miner closed-source. Vertcoin non approva né supporta questi miner. Non possono essere controllati sui loro contenuti e potrebbero contenere funzioni dannose per il tuo computer.", "enable_integrated" : "Mina con la grafica integrata", "enable_integrated_sub" : "Il miner userà la grafica a bordo (se possibile)", "testnet" : "Modalità testnet", "testnet_sub" : "Abilita la modalità testnet. Non minerai monete reali.", "skipverthashverify" : "Non verificare Verthash all'avvio", "skipverthashverify_sub" : "Salta la verifica di integrità del file dei dati di Verthash all'avvio (non raccomandato)", "save_n_restart" : "Salva e Riavvia", "pool" : "Pool di mining" }, "tabbar" : { "wallet" : "Portafoglio", "send_coins" : "Invia monete", "settings" : "Impostazioni" }, "tracking": { "update_available" : "Aggiornamento disponibile", "tracking_enabled" : "Stai condividendo in modo anonimo le statistiche sull'utilizzo", "disable_tracking" : "Disabilita", "tracking_disabled" : "Non stai condividendo le statistiche sull'utilizzo", "enable_tracking" : "Abilitane la condivisione per aiutarci a migliorare la tua esperienza", "report_issue" : "Segnala un problema" }, "update" : { "new_version_available" : "Nuova versione disponibile", "download" : "Scarica" } } ================================================ FILE: frontend/src/i18n/ja.json ================================================ { "generic" : { "retry" : "再試行をする", "back_to_wallet" : "ウォレットに戻る", "close" : "閉じる" }, "welcome" : { "alreadyrunning" : "ワンクリックマイナーはすでに実行中です。複数回実行することはできません。", "click_button_to_start" : "下のボタンをクリックして、マイニングを再開してください。", "startmining" : "マイニングを始める!", "makeapassword" : "入力するパスワードを無くさないでください。", "password" : "パスワード", "confirmpassword" : "パスワード(確認用)", "password_cannot_be_empty" : "パスワードは空にできません", "password_mismatch" : "新パスワードと再入力パスワードが一致しません", "error_initializing" : "ウォレットの初期化中に問題が発生しました" }, "checks" : { "prerequisite" : "前提条件がインストールされています。許可を求めてポップアップが表示されるかもしれません (またはタスクバーで点滅している可能性があります)", "checks_failed" : "チェック失敗", "checking_mining_software" : "マイニングソフトウェアの確認中...", "rapidfail" : "迅速な障害発生の確認中", "compatibility" : "GPUの互換性を確認中...", "installing_miners" : "マイニングソフトウェアのインストール中..." }, "mining" : { "spendable_balance" : "消費可能残高", "still_maturing" : "まだ成熟している", "pending_pool_payout" : "保留中のプールの支払い", "waiting_for_miners" : "マイナーが始まるのを待っている", "expected_earnings_24h" : "予想収益 (24時間)", "estimating" : "見積もり", "stop_mining" : "マイニングをやめる" }, "sending" : { "send_all_to" : "にあなたの採掘したコインをすべて送る", "youre_sending_x_to" : "に{receivedBalance}を送っています", "youre_sending_x_in_y_txs_to" : "{receivedTxCount}トランザクションで{receivedBalance}をに送信しています", "receiver_address" : "受信者アドレス", "wallet_password" : "ウォレットパスワード", "send" : "送る", "coins_sent" : "あなたのコインが送られました!", "view_trans_plural" : "取引を見る", "view_trans_singular" : "取引を見る", "failed_to_send" : "コインを送信できませんでした", "password_required" : "ウォレットパスワードが必要です", "invalid_address" : "無効なアドレス", "script_failure" : "スクリプトの失敗", "could_not_calculate_fee" : "料金を計算できませんでした", "insufficient_funds" : "残高不足", "sign_failed" : "トランザクションに署名できません。 パスワードを確認してください", "send_failed" : "取引を送信できません。 詳細についてはdebug.logを確認してください" }, "settings" : { "enable_debug" : "デバッグを有効にする", "enable_debug_sub" : "マイナーのコンソール出力をデバッグログに含めます。ログをかなり大きくすることができます", "auto_start" : "自動スタート", "auto_start_sub" : "コンピュータにログインしたらワンクリックマイナーを起動する", "closed_source" : "クローズドソースマイナーを使用する", "closed_source_sub" : "ハッシュレートは向上していますが、開発者の費用がかかる未監査のマイナー", "closed_source_warning" : "クローズドソースマイナーを使用することを選択しました。ヴァートコインはこれらのマイナーを支持しません。内容を監査することはできず、コンピュータに害を及ぼす機能が含まれている可能性があります。", "save_n_restart" : "保存して再起動" }, "tabbar" : { "wallet" : "ウォレット", "send_coins" : "コインを送る", "settings" : "設定" }, "tracking": { "update_available" : "アップデート利用可能", "tracking_enabled" : "利用統計を匿名で共有しています", "disable_tracking" : "無効にする", "tracking_disabled" : "利用統計を共有していません", "enable_tracking" : "これらを有効にして利用体験の改善を行いさせるのを助けてください", "report_issue" : "問題を報告する" }, "update" : { "new_version_available" : "新バージョン利用可能", "download" : "ダウンロード" } } ================================================ FILE: frontend/src/i18n/lt.json ================================================ { "generic" : { "retry" : "Bandyti iš naujo", "back_to_wallet" : "Grįžti į piniginę", "close" : "Uždaryti" }, "welcome" : { "alreadyrunning" : "One-Click Miner jau yra paleistas. Jūs negalite jo paleisti daugiau nei vieną kartą.", "click_button_to_start" : "Spauskite žemiau esantį mygtuką norint ir vėl pradėti kasimą.", "startmining" : "Pradėti kasimą!", "makeapassword" : "Sugalvokite OCM piniginės slaptažodį. Nepamirškite jo.", "password" : "Slaptažodis", "confirmpassword" : "Patvirtinti slaptažodį", "password_cannot_be_empty" : "Slaptažodžio laukelis negali būti tuščias", "password_mismatch" : "Slaptažodžiai nesutampa", "error_initializing" : "Įvyko klaida inicijuojant piniginę" }, "checks" : { "prerequisite" : "Diegiami būtiniausi leidimai. Galite išvysti iššokantį langą prašantį leidimo (gali mirgsėti užduočių juostoje)", "checks_failed" : "Patikros nepavyko", "checking_mining_software" : "Tikrinama kasimui skirta programinė įranga...", "rapidfail" : "Tikrinama ar nėra gedimų", "compatibility" : "Tikrinamas vaizdo plokštės suderinamumas...", "installing_miners" : "Įdiegiama kasimui skirta programinė įranga...", "verthash" : "Tikrinamas / kuriamas Verthash duomenų failas..." }, "mining" : { "spendable_balance" : "Išleidžiamas balansas", "still_maturing" : "vis dar bręsta", "pending_pool_payout" : "laukiamas pool išmokėjimas", "pending_payout_info_unavailable" : "Prašome palaukti laukiamo pool išmokėjimo informacijos", "waiting_for_miners" : "Waiting for miners to start", "expected_earnings_24h" : "Numatomas uždarbis (24h)", "estimating" : "vertinama", "stop_mining" : "Stabdyti kasimą", "active_pool" : "Aktyvus pool", "pool_fee" : "mokestis", "copy_address" : "Kopijuoti jūsų piniginės adresą", "payout_information" : "Peržiūrėti išmokėjimo informaciją" }, "sending" : { "send_all_to" : "Siųsti visas jūsų iškastas monetas į", "youre_sending_x_to" : "Jūs siunčiate {receivedBalance} į", "youre_sending_x_in_y_txs_to" : "Jūs siunčiate {receivedBalance} per {receivedTxCount} pervedimus į", "receiver_address" : "Gavėjo adresas", "wallet_password" : "OCM piniginės slaptažodis", "send" : "Siųsti", "coins_sent" : "Jūsų monetos išsiųstos!", "view_trans_plural" : "Peržiūrėti pavedimus", "view_trans_singular" : "Peržiūrėti pavedimą", "failed_to_send" : "Monetų išsiuntimas nesėkmingas", "password_required" : "Būtina nurodyti piniginės slaptažodį", "invalid_address" : "Neteisingas adresas", "script_failure" : "Script gedimas", "could_not_calculate_fee" : "Negalime apskaičiuoti mokeščio", "insufficient_funds" : "Nepakanka lėšų", "sign_failed" : "Negalite patvirtinti pavedimo. Patikrinkite slaptažodį.", "send_failed" : "Negalite išsiųsti pavedimo. Patikrinkite debug.log norint sužinoti daugiau" }, "settings" : { "enable_debug" : "Įgalinti derinimą", "enable_debug_sub" : "Įtraukti kasimo programinės įrangos konsolės išvestį į derinimo žurnalą. Žurnalas gali užaugti gana didelis", "auto_start" : "Automatinis paleidimas", "auto_start_sub" : "Paleisti One-Click Miner kai tik prisijungiate prie kompiuterio", "closed_source" : "Naudoti uždaro kodo programine įranga", "closed_source_sub" : "Greitesnis kasimas, bet galimi papildomi kasimo mokeščiai", "closed_source_warning" : "Jūs pasirinkote uždaro kodo programine įranga kasimui. Vertcoin neskatina ir nepalaiko tokios programinės įrangos. Ji negali būti patikrinta ir gali turėti kompiuteriui žalingų funkcijų.", "enable_integrated" : "Kasti su integruota vaizdo plokšte", "enable_integrated_sub" : "Kasimo programinė įranga naudos integruota vaizdo plokšte (jei įmanoma)", "testnet":"Testnet režimas", "testnet_sub":"Aktyvuoja testnet režimą, kuriame kasite netikras monetas.", "skipverthashverify":"Paleidžiant netikrinti Verthash", "skipverthashverify_sub":"Praleisite Verthash duomenų failo patikrinimą paleidžiant programą (nerekomenduojama)", "save_n_restart" : "Išsaugoti ir perkrauti", "pool": "Kasėjų pool" }, "tabbar" : { "wallet" : "Piniginė", "send_coins" : "Siųsti monetas", "settings" : "Nustatymai" }, "tracking": { "update_available" : "Galimas atnaujinimas", "tracking_enabled" : "Jūs anonimiškai dalinaties naudojimosi statistika", "disable_tracking" : "Išjungti", "tracking_disabled" : "Jūs nesidalinate naudojimosi statistika", "enable_tracking" : "Aktyvuokite naudojimosi statistika ir padėkite mums pagerinti jūsų patirtį", "report_issue" : "Pranešti apie problemą" }, "update" : { "new_version_available" : "Prieinama nauja versija", "download" : "Atsiųsti" } } ================================================ FILE: frontend/src/i18n/nl.json ================================================ { "generic" : { "retry" : "Opnieuw proberen", "back_to_wallet" : "Terug naar portemonnee", "close" : "Sluiten" }, "welcome" : { "alreadyrunning" : "De One-Click Miner is al actief. Je kunt 'm niet meer dan een keer draaien.", "click_button_to_start" : "Klik op de knop om te beginnen met minen.", "startmining" : "Begin met minen!", "makeapassword" : "Kies een wachtwoord. Raak het niet kwijt.", "password" : "Wachtwoord", "confirmpassword" : "Bevestig Wachtwoord", "password_cannot_be_empty" : "Wachtwoord mag niet leeg zijn", "password_mismatch" : "Wachtwoorden komen niet overeen", "error_initializing" : "Er ging iets mis met het initialiseren van de portemonnee" }, "checks" : { "prerequisite" : "Er wordt software geinstalleerd die nodig is voor de OCM. Je zou een pop-up kunnen krijgen die om toestemming vraagt (zou slechts in de taakbalk kunnen knipperen)", "checks_failed" : "Controles mislukt", "checking_mining_software" : "Miner software controleren...", "rapidfail" : "Controleren op het voorkomen van herhaaldelijke fouten", "compatibility" : "Compatibiliteit van grafische kaart controleren...", "installing_miners" : "Miner software installeren...", "verthash" : "Verifieren / creëren Verthash data file..." }, "mining" : { "spendable_balance" : "Besteedbaar saldo", "still_maturing" : "In ontwikkeling", "pending_pool_payout" : "saldo in afwachting bij mining-pool", "waiting_for_miners" : "Wacht tot de miner software opstart", "expected_earnings_24h" : "Verwachte inkomsten (24u)", "estimating" : "berekenen", "stop_mining" : "Stop minen", "active_pool" : "Actieve pool", "pool_fee" : "Vergoeding", "copy_address" : "Kopieer je portemonnee adres", "payout_information" : "Bekijk uitbetaling informatie" }, "sending" : { "send_all_to" : "Verstuur al je coins naar", "youre_sending_x_to" : "Je gaat {receivedBalance} versturen naar", "youre_sending_x_in_y_txs_to" : "Je gaat in {receivedTxCount} transacties {receivedBalance} versturen naar", "receiver_address" : "Adres van ontvanger", "wallet_password" : "Wachtwoord van je OCM portomonnee", "send" : "Versturen", "coins_sent" : "Je coins zijn verstuurd!", "view_trans_plural" : "Bekijk transacties", "view_trans_singular" : "Bekijk transactie", "failed_to_send" : "Je coins versturen is mislukt", "password_required" : "Wachtwoord van je portomonnee is verplicht", "invalid_address" : "Ongeldig adres", "script_failure" : "Scriptfout", "could_not_calculate_fee" : "Kon transactiekosten niet berekenen", "insufficient_funds" : "Onvoldoende saldo", "sign_failed" : "Kon de transactie niet ondertekenen. Controleer het wachtwoord.", "send_failed" : "Kon de transactie niet versturen. Controleer debug.log voor meer informatie" }, "settings" : { "enable_debug" : "Schakel foutopsporing in", "enable_debug_sub" : "Hiermee wordt de uitvoer van de miner software in de debug.log opgenomen. Kan de log bestanden groot maken!", "auto_start" : "Automatisch starten", "auto_start_sub" : "Start de One-Click Miner als je inlogt op je computer", "closed_source" : "Gebruik miners zonder open broncode", "closed_source_sub" : "Betere prestaties, maar de software is niet gecontroleerd en je betaalt een percentage aan de ontwikkelaars", "closed_source_warning" : "Je hebt ervoor gekozen om miners zonder open broncode te gebruiken. Vertcoin ondersteund deze miners niet en raad ze niet aan. De software kan niet op inhoud gecontroleerd worden en kan functies bevatten die je computer beschadigen.", "enable_integrated" : "Minen op geïntegreerde graphics", "enable_integrated_sub" : "De miner gebruikt de geïntegreerde graphics (indien mogelijk)", "testnet":"Testnet modus", "testnet_sub":"Schakelt testnet modus in. Je gaat geen echte coins minen.", "skipverthashverify":"Verifieer Verthash niet bij opstarten", "skipverthashverify_sub":"Zal het verifiëren van de bestandsintegriteit van Verthash-gegevens bij het opstarten overslaan (niet aanbevolen)", "save_n_restart" : "Opslaan & Herstarten", "pool": "Mining pool" }, "tabbar" : { "wallet" : "Portemonnee", "send_coins" : "Verstuur coins", "settings" : "Instellingen" }, "tracking": { "update_available" : "Update beschikbaar", "tracking_enabled" : "Je deelt anonieme gebruiksstatistieken", "disable_tracking" : "Schakel uit", "tracking_disabled" : "Je deelt geen gebruiksstatistieken", "enable_tracking" : "Schakel in om ons te helpen je gebruikservaring te verbeteren", "report_issue" : "Meld een probleem" }, "update" : { "new_version_available" : "Nieuwe versie beschikbaar", "download" : "Downloaden" } } ================================================ FILE: frontend/src/i18n/no.json ================================================ { "generic": { "retry": "Prøv igjen", "back_to_wallet": "tilbake til lommebok", "close": "Lukk" }, "welcome": { "alreadyrunning": "One-Click Miner kjører allerede. Du kan ikke starte et nytt program mens det er et annet i gang.", "click_button_to_start": "Klikk på knappen under for å gjenoppta utvinning.", "startmining": "Start å utvinne!", "makeapassword": "Lag et passord. Ikke mist det.", "password": "Passord", "confirmpassword": "Bekreft passord", "password_cannot_be_empty": "Passord kan ikke stå tomt", "password_mismatch": "Passordene er ikke identiske", "error_initializing": "Noe feilet under initialiseringen av lommeboka" }, "checks": { "prerequisite": "Et nødvendig komponent installeres. Underveis kan du se et popup-vindu som spør om tillatelse (Det kan også bare blinke på oppgavelinjen)", "checks_failed": "Kontrolleringen feilet", "checking_mining_software": "Kontrollerer utvinnings-programmet...", "rapidfail": "Sjekker for hyppige feil", "compatibility": "Sjekker CPU kompatibilitet...", "installing_miners": "Installerer utvinnings-programvare..." }, "mining": { "spendable_balance": "Tilgjengelig saldo", "still_maturing": "Under modning", "pending_pool_payout": "Avventende pool-utbetaling", "waiting_for_miners": "Venter på at utvinnere starter", "expected_earnings_24h": "Forventet gevinst (24t)", "estimating": "Estimerer", "stop_mining": "Stopp utvinning" }, "sending": { "send_all_to": "Send alle utvunnede kryptomynter til", "youre_sending_x_to": "Du sender {beløp} til", "youre_sending_x_in_y_txs_to": "Du sender {receivedBalance} i {receivedTxCount} transaksjon til", "receiver_address": "Mottaksadresse", "wallet_password": "Lommebokpassord", "send": "Send", "coins_sent": "Dine kryptomynter er sendt!", "view_trans_plural": "Se transaksjoner", "view_trans_singular": "Se transaksjon", "failed_to_send": "Sending av kryptomynter mislyktes", "password_required": "Passord for lommebok er påkrevd", "invalid_address": "Ugyldig addresse", "script_failure": "Script feilet", "could_not_calculate_fee": "Kunne ikke regne ut beløp", "insufficient_funds": "Ikke tilstrekkelige midler", "sign_failed" : "Kan ikke signere transaksjonen. Sjekk passordet ditt", "send_failed": "Kunne ikke gjennomføre transaksjonen. Sjekk debug.log for mer informasjon" }, "settings": { "enable_debug": "Aktiver debugging", "enable_debug_sub": "Inkluder OCMs utdata i feilsøkingsloggen. Kan gjøre loggene en del større", "auto_start": "Automatisk start", "auto_start_sub": "Start One-Click Miner når du logger på maskinen", "closed_source": "Bruk utvinningsprogramvare med stengt kildekode", "closed_source_sub": "Bedre hashrate, men ukjente utvinnerprogramvareutvikleren kan ha en avgift for å utvinne", "closed_source_warning": "Du har valgt å bruke utvinningsprogramvare(r) med lukket kildekode. Vertcoin støtter eller verifiserer ikke disse utvinnerne. Deres kildekode kan ikke verifiseres eller godkjennes, og de kan inneholde funksjoner som skader din datamaskin.", "save_n_restart": "Lagre og start på nytt" }, "tabbar": { "wallet": "Lommebok", "send_coins": "Send kryptomynter", "settings": "Innstillinger" }, "tracking": { "update_available": "Oppdatering tilgjengelig", "tracking_enabled": "Du deler brukerstatistikken anonymt", "disable_tracking": "Deaktiver", "tracking_disabled": "Du deler ikke brukerstatistikk", "enable_tracking": "Aktiver tracking for å hjelpe oss å bedre din brukeropplevelse", "report_issue": "Rapporter et problem" }, "update": { "new_version_available": "Ny versjon er tilgjengelig", "download": "Last ned" } } ================================================ FILE: frontend/src/i18n/pa.json ================================================ { "generic" : { "retry" : "ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ", "back_to_wallet" : "ਵਾਪਸ ਬਟੂਆ ਵੱਲ", "close" : "ਨੇੜੇ" }, "welcome" : { "alreadyrunning" : "ਇਕ ਕਲਿਕ ਖੋਜਕਾਰ ਪਹਿਲਾਂ ਹੀ ਚੱਲ ਰਿਹਾ ਹੈ. ਤੁਸੀਂ ਇਸ ਨੂੰ ਇਕ ਤੋਂ ਵੱਧ ਵਾਰ ਨਹੀਂ ਚਲਾ ਸਕਦੇ.", "click_button_to_start" : "ਦੁਬਾਰਾ ਮਾਈਨਿੰਗ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਹੇਠਾਂ ਦਿੱਤੇ ਬਟਨ ਤੇ ਕਲਿਕ ਕਰੋ.", "startmining" : "ਮਾਈਨਿੰਗ ਸ਼ੁਰੂ ਕਰੋ", "makeapassword" : "ਇੱਕ ਪਾਸਵਰਡ ਬਣਾਓ ਇਸ ਨੂੰ ਨਾ ਗੁਆਓ", "password" : "ਪਾਸਵਰਡ", "confirmpassword" : "ਪਾਸਵਰਡ ਪੱਕਾ ਕਰੋ", "password_cannot_be_empty" : "ਪਾਸਵਰਡ ਖਾਲੀ ਨਹੀਂ ਹੋ ਸਕਦਾ", "password_mismatch" : "ਪਾਸਵਰਡ ਮੇਲ ਨਹੀਂ ਖਾਂਦਾ", "error_initializing" : "ਵਾਲਿਟ ਨੂੰ ਸ਼ੁਰੂ ਕਰਦਿਆਂ ਕੁਝ ਗਲਤ ਹੋਇਆ" }, "checks" : { "prerequisite" : "ਇੱਕ ਜ਼ਰੂਰਤ ਲਗਾਈ ਜਾ ਰਹੀ ਹੈ. ਤੁਸੀਂ ਇਕ ਪੌਪ-ਅਪ ਨੂੰ ਅਨੁਮਤੀ ਮੰਗਦੇ ਹੋਏ ਵੇਖ ਸਕਦੇ ਹੋ (ਸਿਰਫ ਟਾਸਕਬਾਰ ਵਿਚ ਝਪਕਣਾ ਹੋ ਸਕਦਾ ਹੈ)", "checks_failed" : "ਜਾਂਚ ਅਸਫਲ", "checking_mining_software" : "ਮਾਈਨਿੰਗ ਸਾੱਫਟਵੇਅਰ ਦੀ ਜਾਂਚ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ", "rapidfail" : "ਤੇਜ਼ੀ ਨਾਲ ਅਸਫਲ ਹੋਣ ਦੀਆਂ ਘਟਨਾਵਾਂ ਦੀ ਜਾਂਚ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ", "compatibility" : "ਗ੍ਰਾਫਿਕਸ ਪ੍ਰੋਸੈਸਿੰਗ ਯੂਨਿਟ ਅਨੁਕੂਲਤਾ ਦੀ ਜਾਂਚ ਕਰ ਰਿਹਾ ਹੈ", "installing_miners" : "ਮਾਈਨਿੰਗ ਸਾੱਫਟਵੇਅਰ ਸਥਾਪਤ ਕਰ ਰਿਹਾ ਹੈ...", "verthash" : "ਪੁਸ਼ਟੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ / ਵਰਥਸ਼ ਫਾਈਲ ਬਣਾਉਣਾ" }, "mining" : { "spendable_balance" : "ਖਰਚ ਕਰਨ ਵਾਲਾ ਸੰਤੁਲਨ", "still_maturing" : "ਅਜੇ ਵੀ ਸਿਆਣਾ", "pending_pool_payout" : "ਬਕਾਇਆ ਪੂਲ ਦਾ ਭੁਗਤਾਨ", "pending_payout_info_unavailable" : "ਕਿਰਪਾ ਕਰਕੇ ਬਕਾਇਆ ਭੁਗਤਾਨ ਜਾਣਕਾਰੀ ਦੀ ਉਡੀਕ ਕਰੋ", "waiting_for_miners" : "ਸ਼ੁਰੂਆਤ ਕਰਨ ਲਈ ਖੋਜਕਾਰ ਦੀ ਉਡੀਕ", "expected_earnings_24h" : "ਉਮੀਦ ਕੀਤੀ ਕਮਾਈ (ਚੋਵੀ ਘੰਟੇ)", "estimating" : "ਅਨੁਮਾਨ ਲਗਾਉਣਾ", "stop_mining" : "ਮਾਈਨਿੰਗ ਰੋਕੋ", "active_pool" : "ਕਿਰਿਆਸ਼ੀਲ ਪੂਲ", "pool_fee" : "ਕਰ", "copy_address" : "ਆਪਣੇ ਵਾਲਿਟ ਪਤੇ ਨੂੰ ਕਾਪੀ ਕਰੋ", "payout_information" : "ਭੁਗਤਾਨ ਜਾਣਕਾਰੀ ਵੇਖੋ" }, "sending" : { "send_all_to" : "ਆਪਣੇ ਸਾਰੇ ਮਾਈਨ ਕੀਤੇ ਸਿੱਕੇ ਭੇਜੋ", "youre_sending_x_to" : "ਤੁਸੀਂ ਭੇਜ ਰਹੇ ਹੋ {receivedBalance} ਨੂੰ", "youre_sending_x_in_y_txs_to" : "ਤੁਸੀਂ ਭੇਜ ਰਹੇ ਹੋ {receivedBalance} ਵਿੱਚ {receivedTxCount} ਨੂੰ ਲੈਣ-ਦੇਣ", "receiver_address" : "ਪ੍ਰਾਪਤ ਕਰਨ ਵਾਲਾ ਪਤਾ", "wallet_password" : "ਵਾਲਿਟ ਪਾਸਵਰਡ", "send" : "ਭੇਜੋ", "coins_sent" : "ਤੁਹਾਡੇ ਸਿੱਕੇ ਭੇਜੇ ਗਏ ਹਨ", "view_trans_plural" : "ਲੈਣ-ਦੇਣ ਵੇਖੋ", "view_trans_singular" : "ਲੈਣ-ਦੇਣ ਵੇਖੋ", "failed_to_send" : "ਤੁਹਾਡੇ ਸਿੱਕੇ ਭੇਜਣ ਵਿੱਚ ਅਸਫਲ", "password_required" : "ਵਾਲਿਟ ਦਾ ਪਾਸਵਰਡ ਲੋੜੀਂਦਾ ਹੈ", "invalid_address" : "ਅਵੈਧ ਪਤਾ", "script_failure" : "ਸਕ੍ਰਿਪਟ ਅਸਫਲ", "could_not_calculate_fee" : "ਫੀਸ ਦੀ ਗਣਨਾ ਨਹੀਂ ਕਰ ਸਕਿਆ", "insufficient_funds" : "ਨਾਕਾਫ਼ੀ ਫੰਡ", "sign_failed" : "ਟ੍ਰਾਂਜੈਕਸ਼ਨ ਤੇ ਦਸਤਖਤ ਕਰਨ ਵਿੱਚ ਅਸਮਰੱਥ. ਆਪਣੇ ਪਾਸਵਰਡ ਦੀ ਜਾਂਚ ਕਰੋ", "send_failed" : "ਲੈਣਦੇਣ ਭੇਜਣ ਵਿੱਚ ਅਸਮਰੱਥ. ਵਧੇਰੇ ਜਾਣਕਾਰੀ ਲਈ ਡੀਬੱਗ.ਲੱਗ ਚੈੱਕ ਕਰੋ" }, "settings" : { "enable_debug" : "ਡੀਬੱਗਿੰਗ ਡੀਬੱਗ", "enable_debug_sub" : "ਡੀਬੱਗ ਲੌਗ ਵਿੱਚ ਖਨਕ ਦੀ ਕੰਸੋਲ ਆਉਟਪੁੱਟ ਸ਼ਾਮਲ ਕਰੋ. ਤੁਹਾਡੇ ਲੌਗਸ ਕਾਫ਼ੀ ਵੱਡੇ ਹੋ ਸਕਦੇ ਹਨ", "auto_start" : "ਆਟੋ ਸਟਾਰਟt", "auto_start_sub" : "ਜਦੋਂ ਤੁਸੀਂ ਆਪਣੇ ਕੰਪਿ inਟਰ ਤੇ ਲੌਗ ਇਨ ਕਰਦੇ ਹੋ ਤਾਂ ਵਨ-ਕਲਿਕ ਖਨਕ ਚਾਲੂ ਕਰੋ", "closed_source" : "ਬੰਦ ਸਰੋਤ ਖੋਜਕਰਤਾ", "closed_source_sub" : "ਬਿਹਤਰ ਹੈਸ਼ਰੇਟ, ਪਰ ਅਣਚਾਹੇ ਖਨਕ ਜੋ ਇੱਕ ਵਿਕਾਸਕਰਤਾ ਦੀ ਫੀਸ ਲੈਂਦੇ ਹਨ", "closed_source_warning" : "ਤੁਸੀਂ ਬੰਦ ਸਰੋਤ ਖਨਕ ਦੀ ਵਰਤੋਂ ਕਰਨ ਦੀ ਚੋਣ ਕੀਤੀ ਹੈ. ਵਰਟਕੋਇਨ ਇਨ੍ਹਾਂ ਮਾਈਨਰਾਂ ਨੂੰ ਸਮਰਥਨ ਨਹੀਂ ਦਿੰਦਾ. ਉਨ੍ਹਾਂ ਦੀ ਸਮਗਰੀ 'ਤੇ ਆਡਿਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ ਅਤੇ ਦੇ ਫੰਕਸ਼ਨ ਹੋ ਸਕਦੇ ਹਨ ਜੋ ਤੁਹਾਡੇ ਕੰਪਿcਟਰ ਨੂੰ ਨੁਕਸਾਨ ਪਹੁੰਚਾ ਸਕਦੇ ਹਨ", "enable_integrated" : "ਏਕੀਕ੍ਰਿਤ ਗਰਾਫਿਕਸ 'ਤੇ ਮੇਰਾ", "enable_integrated_sub" : "ਖਨਕ ਆਨਬੋਰਡ ਗ੍ਰਾਫਿਕਸ ਦੀ ਵਰਤੋਂ ਕਰੇਗਾ (ਜੇ ਮੁਮਕਿਨ)", "testnet":"ਟੈਸਟਨੈੱਟ ਮੋਡ", "testnet_sub":"ਟੈਸਟਨੈੱਟ ਮੋਡ ਨੂੰ ਸਮਰੱਥ ਬਣਾਉਂਦਾ ਹੈ. ਤੁਸੀਂ ਅਸਲੀ ਸਿੱਕਿਆਂ ਦੀ ਖੁਦਾਈ ਨਹੀਂ ਕਰੋਗੇ.", "skipverthashverify":"ਪੁਸ਼ਟੀ ਨਾ ਕਰੋ ਵਰਥਸ਼ ਸ਼ੁਰੂ 'ਤੇ", "skipverthashverify_sub":"ਦੀ ਫਾਈਲ ਇਕਸਾਰਤਾ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨਾ ਛੱਡ ਦਿੱਤਾ ਜਾਵੇਗਾ ਵਰਥਸ਼ ਸ਼ੁਰੂਆਤ 'ਤੇ ਡਾਟਾ (ਦੀ ਸਿਫਾਰਸ਼ ਨਹੀਂ ਕੀਤੀ ਜਾਂਦੀ)", "save_n_restart" : "ਸੇਵ ਅਤੇ ਰੀਸਟਾਰਟ", "pool": "ਮਾਈਨਿੰਗ ਪੂਲ" }, "tabbar" : { "wallet" : "ਬਟੂਆ", "send_coins" : "ਸਿੱਕੇ ਭੇਜੋ", "settings" : "ਸੈਟਿੰਗਜ਼" }, "tracking": { "update_available" : "ਅਪਡੇਟ ਉਪਲਬਧ ਹੈ", "tracking_enabled" : "ਤੁਸੀਂ ਅਗਿਆਤ ਤੌਰ ਤੇ ਵਰਤੋਂ ਦੇ ਅੰਕੜੇ ਸਾਂਝੇ ਕਰ ਰਹੇ ਹੋ", "disable_tracking" : "ਅਯੋਗ", "tracking_disabled" : "ਤੁਸੀਂ ਵਰਤੋਂ ਦੇ ਅੰਕੜਿਆਂ ਨੂੰ ਸਾਂਝਾ ਨਹੀਂ ਕਰ ਰਹੇ", "enable_tracking" : "ਆਪਣੇ ਤਜ਼ਰਬੇ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਣ ਵਿਚ ਸਾਡੀ ਸਹਾਇਤਾ ਕਰਨ ਲਈ ਇਨ੍ਹਾਂ ਨੂੰ ਸਮਰੱਥ ਕਰੋ", "report_issue" : "ਇੱਕ ਮੁੱਦੇ ਦੀ ਰਿਪੋਰਟ ਕਰੋ" }, "update" : { "new_version_available" : "ਨਵਾਂ ਵਰਜਨ ਉਪਲਬਧ ਹੈ", "download" : "ਡਾ .ਨਲੋਡ" } } ================================================ FILE: frontend/src/i18n/pl.json ================================================ { "generic" : { "retry" : "Ponów", "back_to_wallet" : "Wróć do portfela", "close" : "Zamknij" }, "welcome" : { "alreadyrunning" : "One-Click Miner jest już uruchomiony. Nie możesz uruchomić kolejnego.", "click_button_to_start" : "Naciśnij przycisk poniżej, aby rozpocząć kopanie.", "startmining" : "Start kopania!", "makeapassword" : "Utwórz hasło. Nie zapomnij go.", "password" : "Hasło", "confirmpassword" : "Potwierdź hasło", "password_cannot_be_empty" : "Hasło nie może być puste", "password_mismatch" : "Hasła nie pasują do siebie", "error_initializing" : "Coś poszło nie tak podczas inicjalizacji portfela" }, "checks" : { "prerequisite" : "Instalowanie dodatków. Może pojawić się prośba o nadanie uprawnień (podświetlona na pasku zadań)", "checks_failed" : "Sprawdzenie zakończyło się niepowodzeniem", "checking_mining_software" : "Sprawdzanie oprogramowania do kopania...", "rapidfail" : "Sprawdzanie komunikatów o błędach", "compatibility" : "Sprawdzanie kompatybilności karty graficznej...", "installing_miners" : "Instalowanie oprogramowania...", "verthash" : "Weryfikowanie / Tworzenie pliku danych Verthash..." }, "mining" : { "spendable_balance" : "Kwota do wydania", "still_maturing" : "nadal dojrzewa", "pending_pool_payout" : "wypłata w trakcie", "pending_payout_info_unavailable" : "Poczekaj na informacje o oczekujących wypłatach", "waiting_for_miners" : "Oczekiwanie na uruchomienie kopania", "expected_earnings_24h" : "Oczekiwany dochód (24h)", "estimating" : "około", "stop_mining" : "Zatrzymaj kopanie", "active_pool" : "Aktywna kopalnia", "pool_fee" : "Prowizja", "copy_address" : "Kopiuj adres swojego portfela" }, "sending" : { "send_all_to" : "Wyślij wszystkie monety do", "youre_sending_x_to" : "Wysyłasz {receivedBalance} do", "youre_sending_x_in_y_txs_to" : "Wysyłasz {receivedBalance} w {receivedTxCount} transakcjach do", "receiver_address" : "Adres odbiorcy", "wallet_password" : "Hasło portfela", "send" : "Wyślij", "coins_sent" : "Monety wysłane!", "view_trans_plural" : "Zobacz transakcje", "view_trans_singular" : "Zobacz transakcję", "failed_to_send" : "Wysyłka monet nieudana", "password_required" : "Podaj hasło do portfela", "invalid_address" : "Błędny adres", "script_failure" : "Błąd skryptu", "could_not_calculate_fee" : "Obliczenie prowizji nie powiodło się", "insufficient_funds" : "Za mało środków", "sign_failed" : "Nie można podpisać transakcji. Sprawdź swoje hasło.", "send_failed" : "Wysyłka nieudana. Więcej informacji w pliku debug.log" }, "settings" : { "enable_debug" : "Włącz debugging", "enable_debug_sub" : "Dołącz komunikaty z kopania do pliku debug.log. Plik może zrobić się duży", "auto_start" : "Automatyczne uruchamianie", "auto_start_sub" : "Uruchom One-Click Miner kiedy uruchamiasz komputer", "closed_source" : "Używaj górników typu closed-source", "closed_source_sub" : "Większa wydajność, ale oprogramowanie może pobierać dodatkową opłatę dla jego autora", "closed_source_warning" : "Zaznaczyłeś używanie górników typu closed-source. Vertcoin nie popiera i nie wspiera tych górników. Ich kod źródłowy nie jest publicznie dostępny i nie można sprawdzić, czy nie zawierają szkodliwego oprogramowania.", "enable_integrated" : "Kop na zintegrowanych kartach graficznych", "enable_integrated_sub" : "Zintegrowane karty graficzne będą wykorzystywane do kopania (jeśli możliwe)", "testnet" : "Tryb Testnet", "testnet_sub" : "Włącza tryb Testnet. Nie będziesz kopał prawdziwych monet.", "skipverthashverify" : "Nie weryfikuj Verthash podczas uruchamiania", "skipverthashverify_sub" : "Weryfikacja integralności pliku verthash.dat podczas uruchamiania zostanie pominięta (niezalecane)", "save_n_restart" : "Zapisz i Wznów", "pool" : "Kopalnia" }, "tabbar" : { "wallet" : "Portfel", "send_coins" : "Wyślij monety", "settings" : "Ustawienia" }, "tracking": { "update_available" : "Dostępne uaktualnienie", "tracking_enabled" : "Udostępnianie statystyk włączone", "disable_tracking" : "Wyłącz", "tracking_disabled" : "Udostępnianie statystyk wyłączone", "enable_tracking" : "Włącz statystyki, by pomóc nam w rozwoju programu", "report_issue" : "Zgłoś problem z programem" }, "update" : { "new_version_available" : "Dostępna nowa wersja", "download" : "Pobierz" } } ================================================ FILE: frontend/src/i18n/pt.json ================================================ { "generic" : { "retry" : "Tentar novamente", "back_to_wallet" : "Regressar à carteira", "close" : "Fechar" }, "welcome" : { "alreadyrunning" : "O One-Click Miner já está em execução. Não pode executá-lo mais do que uma vez.", "click_button_to_start" : "Clique no botão abaixo para iniciar a mineração novamente.", "startmining" : "Começar a Minar!", "makeapassword" : "Faça uma palavra-passe para a Carteira do OCM. Não a perca!", "password" : "Palavra-passe", "confirmpassword" : "Confirme a palavra-passe", "password_cannot_be_empty" : "É necessário inserir uma palavra-passe", "password_mismatch" : "As palavras-passe não coincidem", "error_initializing" : "Houve um erro ao iniciar a carteira" }, "checks" : { "prerequisite" : "Os pré-requisitos estão a ser instalados. Podem haver popups a pedir permissões (poderá ser um icon a piscar na barra de trefas)", "checks_failed" : "As verificações falharam", "checking_mining_software" : "A verificar o sfotware de mineração...", "rapidfail" : "A verificar ocorrências de falhas rápidas", "compatibility" : "A verificar a compatibilidade da GPU...", "installing_miners" : "A instalar o software de mineração...", "verthash" : "A verificar / criar o ficheiro de dados do Verthash..." }, "mining" : { "spendable_balance" : "Quantia disponível", "still_maturing" : "aguarda confirmações", "pending_pool_payout" : "pagamento pela pool pendente", "pending_payout_info_unavailable" : "Espere pela informação sobre os pagamentos pendentes", "waiting_for_miners" : "Esperando pela inicialização dos mineiros", "expected_earnings_24h" : "Ganhos Esperados (24h)", "estimating" : "a estimar", "stop_mining" : "Parar de Minar", "active_pool" : "Pool ativa", "pool_fee" : "taxa", "copy_address" : "Copiar o endereço da sua carteira", "payout_information" : "Ver informação do pagamento" }, "sending" : { "send_all_to" : "Envie todas as suas moedas minadas para", "youre_sending_x_to" : "Está a enviar {receivedBalance} para", "youre_sending_x_in_y_txs_to" : "Está a enviar {receivedBalance} em {receivedTxCount} transações para", "receiver_address" : "Endereço do Destinatário", "wallet_password" : "Palavra-passe da Carteira do OCM", "send" : "Enviar", "coins_sent" : "As suas moedas foram enviadas!", "view_trans_plural" : "Ver transações", "view_trans_singular" : "Ver transação", "failed_to_send" : "Ocorreu um erro ao tentar enviar as suas moedas", "password_required" : "A palavra-passe da carteira é necessária", "invalid_address" : "Endereço inválido", "script_failure" : "Erro no script", "could_not_calculate_fee" : "Não foi possível calcular a taxa", "insufficient_funds" : "Montante disponível insuficiente", "sign_failed" : "Não foi possível assinar a transação. Verifique sua palavra-passe", "send_failed" : "Não foi possível efetuar a transação. Veja o debug.log para mais informações." }, "settings" : { "enable_debug" : "Ativar o debugging", "enable_debug_sub" : "Incluir o output da consola dos mineiros no debug.log. Isto pode aumentar bastante o tamanho dos logs", "auto_start" : "Iniciar automaticamente", "auto_start_sub" : "Iniciar o One-Click Miner ao iniciar sessão no computador", "closed_source" : "Utilizar mineiros que são closed-source", "closed_source_sub" : "Têm melhor hashrate, mas são mineiros não examinados e têm taxas de utilização", "closed_source_warning" : "Selecionou a utilização de mineiros closed-source. O Vertcoin não apoia ou suporta estes mineiros. O seu conteúdo não pode ser verificado e, por isso, podem conter funções que podem afetar o seu computador.", "enable_integrated" : "Minerar na placa gráfica integrada", "enable_integrated_sub" : "O minerador usará a placa gráfica integrada (se possível)", "testnet":"Modo de teste de rede", "testnet_sub":"Ativar modo de teste de rede. Você não estará minerando moedas reais.", "skipverthashverify":"Não verificar o Verthash na inicialização", "skipverthashverify_sub":"Irá pular a verificação da integridade do arquivo dos dados do Verthash na inicialização (não recomendado)", "save_n_restart" : "Gravar & Reiniciar", "pool": "Pool de Mineração" }, "tabbar" : { "wallet" : "Carteira", "send_coins" : "Enviar moedas", "settings" : "Configurações" }, "tracking": { "update_available" : "Atualização disponível", "tracking_enabled" : "Está a partilhar estatísticas de utilização anonimamente", "disable_tracking" : "Desativar", "tracking_disabled" : "Não está a partilhar estatísticas de mineração", "enable_tracking" : "Ative-as para que possamos melhorar a sua experiência", "report_issue" : "Reportar um problema" }, "update" : { "new_version_available" : "Nova versão disponível", "download" : "Descarregar" } } ================================================ FILE: frontend/src/i18n/ro.json ================================================ { "generic" : { "retry" : "Încearcă din nou", "back_to_wallet" : "Înapoi la portofel", "close" : "Închide" }, "welcome" : { "alreadyrunning" : "Minerul One-Click este deja pornit. Poți să îl pornești doar o singură dată.", "click_button_to_start" : "Apasă pe butonul de mai jos pentru a reîncepe să minezi.", "startmining" : "Începe să minezi!", "makeapassword" : "Creează o parolă. Nu o pierde.", "password" : "Parolă", "confirmpassword" : "Confirmă parola", "password_cannot_be_empty" : "Câmpul Parolă nu poate fi gol", "password_mismatch" : "Parolele trebuie să fie identice", "error_initializing" : "A apărut o eroare la inițializarea portofelului." }, "checks" : { "prerequisite" : "Se instalează un pachet software necesar. Este posibil să apară o fereastră care cere permisiuni (verifică dacă îți clipește în bara de activități)", "checks_failed" : "Verificări eșuate", "checking_mining_software" : "Se verifică software-ul de minare...", "rapidfail" : "Se verifică dacă există erori rapide (i.e. programul se oprește imediat după ce a pornit)", "compatibility" : "Se verifică compatibilitatea cu placa grafică...", "installing_miners" : "Se instalează software-ul de minare..." }, "mining" : { "spendable_balance" : "Balanță disponibilă", "still_maturing" : "în curs de maturizare", "pending_pool_payout" : "plată de la pool în curs de efectuare", "waiting_for_miners" : "Se așteaptă pornirea minerilor", "expected_earnings_24h" : "Câștiguri așteptate (24h)", "estimating" : "se estimează", "stop_mining" : "Oprește minarea" }, "sending" : { "send_all_to" : "Trimite toate monedele minate către", "youre_sending_x_to" : "Vei trimite {receivedBalance} către", "youre_sending_x_in_y_txs_to" : "Vei trimite {receivedBalance} în {receivedTxCount} tranzacții către", "receiver_address" : "Adresa destinatarului", "wallet_password" : "Parola portofelului", "send" : "Trimite", "coins_sent" : "Monedele au fost trimise!", "view_trans_plural" : "Vizualizare tranzacții", "view_trans_singular" : "Vizualizare tranzacție", "failed_to_send" : "Monedele nu au putut fi trimise!", "password_required" : "Trebuie să introduci parola portofelului", "invalid_address" : "Adresă invalidă", "script_failure" : "Eroare de script", "could_not_calculate_fee" : "Taxa pe tranzacție nu a putut fi calculată", "insufficient_funds" : "Fonduri insuficiente", "sign_failed":"Imposibil de semnat tranzacția. Verificați parola", "send_failed" : "Nu se poate trimite tranzacția. Verifică fișierul debug.log pentru informații suplimentare" }, "settings" : { "enable_debug" : "Activează depanarea", "enable_debug_sub" : "Include textul din consola minerului în logul de depanare. Aceast lucru poate duce la o creștere semnificativă a dimensiunii logurilor", "auto_start" : "Pornire automată", "auto_start_sub" : "Pornește Minerul One-Click când te loghezi pe calculator", "closed_source" : "Folosește mineri closed-source", "closed_source_sub" : "Au hashrate mai bun, dar sunt mineri neauditați care trimit o taxă către dezvoltator", "closed_source_warning" : "Ai ales să folosești mineri closed-source. Vertcoin nu este afiliat cu acești mineri și nu oferă suport pentru ei. Conținutul lor nu poate fi auditat și ar putea include funcționalități dăunătoare pentru calculatorul tău.", "save_n_restart" : "Salvează și repornește" }, "tabbar" : { "wallet" : "Portofel", "send_coins" : "Trimite monede", "settings" : "Setări" }, "tracking": { "update_available" : "Actualizare disponibilă", "tracking_enabled" : "Împărtășești statistici de utilizare în mod anonim", "disable_tracking" : "Dezactivează", "tracking_disabled" : "Nu împărtășești statistici de utilizare", "enable_tracking" : "Activează această setare pentru a ne ajuta să îți oferim o experiență mai bună", "report_issue" : "Raportează o problemă" }, "update" : { "new_version_available" : "O nouă versiune este disponibilă", "download" : "Descarcă" } } ================================================ FILE: frontend/src/i18n/ru.json ================================================ { "generic" : { "retry" : "Попробовать снова", "back_to_wallet" : "Вернуться в кошелёк", "close" : "Закрыть" }, "welcome" : { "alreadyrunning" : "One-Click Miner уже запущен. Вы не можете запустить его более одного раза.", "click_button_to_start" : "Нажмите кнопку ниже, чтобы начать майнинг снова.", "startmining" : "Начать майнинг!", "makeapassword" : "Создайте пароль. Не потеряйте его!", "password" : "Пароль", "confirmpassword" : "Подтвердите пароль", "password_cannot_be_empty" : "Пароль не может быть пустым", "password_mismatch" : "Пароли не совпадают", "error_initializing" : "Что-то пошло не так при инициализации кошелька" }, "checks" : { "prerequisite" : "Компоненты устанавливаются. Вы можете увидеть всплывающее окно с запросом разрешений (оно может просто мигать на панели задач)", "checks_failed" : "Сбой при проверке", "checking_mining_software" : "Проверка майнеров...", "rapidfail" : "Проверка случаев быстрой неисправности", "compatibility" : "Проверка совместимости GPU...", "installing_miners" : "Установка майнеров...", "verthash" : "Проверка / создание файла данных Verthash ..." }, "mining" : { "spendable_balance" : "Доступный баланс", "still_maturing" : "Ожидает подтверждений", "pending_pool_payout" : "Ожидание выплаты с пула", "pending_payout_info_unavailable" : "Дождитесь информации об ожидающих выплатах", "waiting_for_miners" : "Ждем майнеры для старта", "expected_earnings_24h" : "Ожидаемый доход (24Ч)", "estimating" : "идет расчет", "stop_mining" : "Закончить майнинг", "active_pool" : "Активный пул", "pool_fee" : "комиссия", "copy_address" : "Скопировать адрес кошелька", "payout_information" : "Посмотреть информацию о выплатах" }, "sending" : { "send_all_to" : "Отправить всю криптовалюту на", "youre_sending_x_to" : "Вы отправляете {receivedBalance} на", "youre_sending_x_in_y_txs_to" : "Вы отправляете {receivedBalance} в {receivedTxCount} транзакции на", "receiver_address" : "Адрес получателя", "wallet_password" : "Пароль от кошелька", "send" : "Отправить", "coins_sent" : "Криптовалюта отправлена!", "view_trans_plural" : "Просмотр транзакций", "view_trans_singular" : "Просмотр транзакции", "failed_to_send" : "Не удалось отправить криптовалюту", "password_required" : "Необходим пароль от кошелёка", "invalid_address" : "Неправильный адрес", "script_failure" : "Ошибка скрипта", "could_not_calculate_fee" : "Не удалось рассчитать комиссию", "insufficient_funds" : "Недостаточно средств", "sign_failed" : "Невозможно подписать транзакцию. Проверьте пароль.", "send_failed" : "Невозможно отправить транзакцию. Проверьте debug.log для получения дополнительной информации" }, "settings" : { "enable_debug" : "Включить отладку", "enable_debug_sub" : "Включите вывод из консоли майнера в журнал отладки. Может значительно увеличить размер файла журнала", "auto_start" : "Авто Старт", "auto_start_sub" : "Запускать One-Click Miner при загрузке компьютера", "closed_source" : "Использовать проприетарные майнеры", "closed_source_sub" : "Лучший хэшрейт, но некоторые майнеры могут взымать комиссию разработчикам", "closed_source_warning" : "Вы решили использовать проприетарные майнеры. Vertcoin не оказывает поддержку этих майнеров. Их содержание не может быть проверено и может включать функции, которые повреждают ваш компьютер.", "enable_integrated" : "Майнить на встроенной видеокарте", "enable_integrated_sub" : "Майнер будет использовать встроенную видеокарту (если возможно)", "testnet" : "Режим тестовой сети", "testnet_sub" : "Включен режим тестовой сети. Вы не получите реальную криптовалюту.", "skipverthashverify" : "Не проверять файл данных Verthash при запуске", "skipverthashverify_sub" : "Пропустить проверку целостности файла Verthash.dat при запуске (не рекомендуется)", "save_n_restart" : "Сохранить и перезапустить", "pool": "Пул для майнинга" }, "tabbar" : { "wallet" : "Кошелёк", "send_coins" : "Отправить криптовалюту", "settings" : "Настройки" }, "tracking": { "update_available" : "Доступно обновление", "tracking_enabled" : "Вы анонимно делитесь статистикой использования майнера", "disable_tracking" : "Запретить", "tracking_disabled" : "Вы не делитесь статистикой использования майнера", "enable_tracking" : "Включить эту опцию, чтобы помочь нам улучшить майнер", "report_issue" : "Сообщить о проблеме" }, "update" : { "new_version_available" : "Доступна новая версия", "download" : "Скачать" } } ================================================ FILE: frontend/src/i18n/sl.json ================================================ { "generic" : { "retry" : "Poskusi znova", "back_to_wallet" : "Nazaj v denarnico", "close":"Zapri" }, "welcome" : { "alreadyrunning" : "One-Click Miner že teče. Ne morete ga zagnati več kot enkrat.", "click_button_to_start" : "Za ponoven začetek rudarjenja pritisni spodnji gumb.", "startmining" : "Začni rudariti!", "makeapassword" : "Ustvari geslo. Ne izgubi ga!", "password" : "Geslo", "confirmpassword" : "Potrdi geslo", "password_cannot_be_empty" : "Polje geslo ne sme biti prazno", "password_mismatch" : "Geslo se ne ujema", "error_initializing" : "Napaka pri nalaganju denarnice" }, "checks" : { "prerequisite" : "Nameščanje predpogojnih datotek. Morda boste videli pojavno okno, ki zahteva dovoljenja (lahko samo utripa v opravilni vrstici)", "checks_failed" : "Preverjanje neuspešno", "checking_mining_software" : "Preverjanje programske opreme za rudarstvo ...", "rapidfail" : "Preverjanje hitrih okvar", "compatibility" : "Preverjanje združljivosti grafične kartice ...", "installing_miners" : "Nameščanje rudarske programske opreme ..." }, "mining" : { "spendable_balance" : "Razpoložljivi znesek", "still_maturing" : "v fazi zorenja", "pending_pool_payout" : "čakajoče pool izplačilo", "waiting_for_miners" : "Čakanje na rudarsko programsko opremo za začetek dela", "expected_earnings_24h" : "Pričakovani zaslužek (24h)", "estimating" : "ocenjevanje", "stop_mining" : "Prekini rudarjenje" }, "sending" : { "send_all_to" : "Pošlji vse izrudarjene kovance na", "youre_sending_x_to" : "Pošiljaš {receivedBalance} na", "youre_sending_x_in_y_txs_to" : "Pošiljaš {receivedBalance} v {receivedTxCount} transakcij na", "receiver_address" : "Naslov prejemnika", "wallet_password" : "Geslo denarnice", "send" : "Pošlji", "coins_sent" : "Kovanci so poslani!", "view_trans_plural" : "Ogled transakcij", "view_trans_singular" : "Ogled transakcije", "failed_to_send" : "Kovancev ni bilo mogoče poslati", "password_required" : "Potrebno je geslo za denarnico", "invalid_address" : "Napačen naslov", "script_failure" : "Napaka skripte", "could_not_calculate_fee" : "Ni bilo mogoče izračunati pristojbine", "insufficient_funds" : "Nezadostna sredstva", "sign_failed":"Transakcije ni mogoče podpisati. Preverite geslo", "send_failed" : "Transakcije ni bilo mogoče poslati. Poglej debug.log za več informacij" }, "settings" : { "enable_debug" : "Omogoči iskanje napak", "enable_debug_sub" : "Omogoči izvoz dnevnikov konzol rudarske programske opreme v dnevnik za odstranjevanje hroščev. Velikost dnevnika se lahko znatno poveča", "auto_start" : "Samodejni zagon", "auto_start_sub" : "Zaženi One-Click Miner, ko se prijaviš v računalnik.", "closed_source" : "Uporabi zaprto programsko opremo za rudarstvo", "closed_source_sub" : "Boljši hashrate, vendar neocenjena programska opema, ki lahko vključuje pristojbino za razvojne inženirje", "closed_source_warning" : "Izbrali ste zaprto programsko opremo za rudarstvo. Vertcoin ne odobrava ali podpira njihovo uporabo. Ne more se preveriti njihovo vsebino tako, da lahko potencijalno vsebuje funkcije, ki lahko poškoduje vaš računalnik.", "save_n_restart" : "Shrani & Znova zaženi" }, "tabbar" : { "wallet" : "Denarnica", "send_coins" : "Pošlji kovance", "settings" : "Nastavitve" }, "tracking": { "update_available" : "Posodobitev je na voljo", "tracking_enabled" : "Anonimno izmenjujete statistične podatke o uporabi", "disable_tracking" : "Onemogoči", "tracking_disabled" : "Ne delite statistike o uporabi", "enable_tracking" : "Omogočite jih, da bi nam pomagali izboljšati vaše izkušnje", "report_issue" : "Prijavite težavo" }, "update" : { "new_version_available" : "Na voljo je nova različica", "download" : "Prenos" } } ================================================ FILE: frontend/src/i18n/sv.json ================================================ { "generic": { "retry": "Försök igen", "back_to_wallet": "Tillbaka till plånboken", "close": "Stäng" }, "welcome": { "alreadyrunning": "One-Click Miner har redan startats. Du kan bara köra en instans åt gången.", "click_button_to_start": "Klicka på knappen nedan för att börja mina igen.", "startmining": "Börja Mina!", "makeapassword": "Skapa ett lösenord. Tappa inte bort det.", "password": "Lösenord", "confirmpassword": "Bekräfta lösenord", "password_cannot_be_empty": "Du måste ange ett lösenord", "password_mismatch": "Lösenorden matchar inte", "error_initializing": "Något gick fel under initialisering av plånboken" }, "checks": { "prerequisite": "Mjukvara som krävs av One-Click Miner installeras. Du kanske kommer se en popup som ber om behörigheter (kanske bara blinkar i aktivitetsfältet)", "checks_failed": "Kontrollerna misslyckades", "checking_mining_software": "Kontrollerar mining mjukvaran...", "rapidfail": "Kontrollerar för förekomsten av fel", "compatibility": "Kontrollerar GPU-kompatibilitet...", "installing_miners": "Installera mining mjukvara...", "verthash": "Verifierar / skapar Verthash data fil..." }, "mining": { "spendable_balance": "Spenderbart saldo", "still_maturing": "mognar fortfarande", "pending_pool_payout": "väntande poolutbetalning", "pending_payout_info_unavailable": "Var vänlig vänta på utbetalnings information", "waiting_for_miners": "Väntar på att miningen ska börja", "expected_earnings_24h": "Förväntade intäkter (24h)", "estimating": "beräknar", "stop_mining": "Sluta Mina", "active_pool": "Aktiv pool", "pool_fee": "avgift", "copy_address": "Kopiera din plånboks address", "payout_information": "Visa utbetalnings information" }, "sending": { "send_all_to": "Skicka dina minade mynt till", "youre_sending_x_to": "Du skickar {receivedBalance} till", "youre_sending_x_in_y_txs_to": "Du skickar {receivedBalance} i {receivedTxCount} transaktion till", "receiver_address": "Mottagaradress", "wallet_password": "Plånbokslösenord", "send": "Skicka", "coins_sent": "Dina mynt har skickats!", "view_trans_plural": "Visa transaktioner", "view_trans_singular": "Visa transaktion", "failed_to_send": "Det gick inte att skicka dina mynt", "password_required": "Plånbokslösenord krävs", "invalid_address": "Ogiltig adress", "script_failure": "Script fel", "could_not_calculate_fee": "Kunde inte beräkna avgift", "insufficient_funds": "Otillräckliga saldo", "sign_failed": "Det gick inte att skriva under transaktion. Kontrollera ditt lösenord", "send_failed": "Det gick inte att skicka transaktion. Kontrollera debug.log filen för mer information" }, "settings": { "enable_debug": "Aktivera felsökning", "enable_debug_sub": "Inkludera mining mjukvarans utdata i felsökningsloggen. Kan gör att loggfilen växer snabbt", "auto_start": "Autostart", "auto_start_sub": "Starta One-Click Miner när du loggar in på din dator", "closed_source": "Använd mining mjukvara med stängd källkod (Proprietär programvara)", "closed_source_sub": "Ger bättre hashrate, men källkoden är ej granskad och mjukvaran har en avgift till utvecklaren", "closed_source_warning": "Du har valt att använda mining mjukvara med sluten källkod. Vertcoin stöder inte denna mjukvara. Källkoden kan inte granskas och kan inehålla delar som skadar din dator.", "enable_integrated": "Använd integrerat grafikkort (iGPU) för att mina", "enable_integrated_sub": "Kommer att använda integrerat grafikkort (om möjligt)", "testnet": "Testnet läge", "testnet_sub": "Aktivera testnet läge. Du kommer inte mina riktiga mynt.", "skipverthashverify": "Verifiera inte verthash under start", "skipverthashverify_sub": "Kommer inte att verifiera verthash data file under uppstart (rekomenderas inte)", "save_n_restart": "Spara & starta om", "pool": "Mining pool" }, "tabbar": { "wallet": "Plånbok", "send_coins": "Skicka mynt", "settings": "Inställningar" }, "tracking": { "update_available": "Uppdatering tillgänglig", "tracking_enabled": "Du delar anonym användningsstatistik", "disable_tracking": "Inaktivera", "tracking_disabled": "Du delar inte användningsstatistik", "enable_tracking": "Aktivera dessa för att hjälpa oss förbättra One-Click Miner", "report_issue": "Rapportera ett problem" }, "update": { "new_version_available": "Ny version tillgänglig", "download": "Ladda ner" } } ================================================ FILE: frontend/src/i18n/tr.json ================================================ { "generic" : { "retry" : "Tekrar dene", "back_to_wallet" : "Cüzdana dön", "close" : "Kapat" }, "welcome" : { "alreadyrunning" : "Tek-tıkta-madencilik zaten çalışıyor. Birden fazla açamazsınız.", "click_button_to_start" : "Madenciliğe devam etmek için butona tıklayın.", "startmining" : "Madenciliğe Başla!", "makeapassword" : "Bir şifre oluşturun. Bu şifreyi unutmayın!", "password" : "Şifre", "confirmpassword" : "Şifreyi tekrar gir", "password_cannot_be_empty" : "Şifreyi boş bırakamazsınız", "password_mismatch" : "Şifreler eşleşmiyor", "error_initializing" : "Cüzdanı başlatırken bir şeyler ters gitti." }, "checks" : { "prerequisite" : "Gerekli bir program yükleniyor. İzin isteyen bir pencere açılabilir (ya da görev çubuğunuz renk değiştirebilir)", "checks_failed" : "Kontroller başarısız oldu.", "checking_mining_software" : "Madencilik yazılımı kontrol ediliyor...", "rapidfail" : "Ani oluşan hatalar kontrol ediliyor...", "compatibility" : "GPU uyumluluğu kontrol ediliyor...", "installing_miners" : "Madencilik yazılımı yükleniyor...", "verthash" : "Verthash dosyası onaylanıyor / oluşturuluyor..." }, "mining" : { "spendable_balance" : "Harcanabilir Bakiye", "still_maturing" : "Olgunlaşmakta olan:", "pending_pool_payout" : "havuz ödemesi bekleniyor", "pending_payout_info_unavailable" : "Beklenen ödeme bilgisi için lütfen bekleyin...", "waiting_for_miners" : "Madencinin başlaması bekleniyor", "expected_earnings_24h" : "Tahmini Kazanç (24 saat)", "estimating" : "Tahmin ediliyor...", "stop_mining" : "Madenciliği durdur", "active_pool" : "Aktif havuz", "pool_fee" : "Havuz komisyonu", "copy_address" : "Cüzdan adresinizi kopyalayın" }, "sending" : { "send_all_to" : "Kazdığım tüm coinleri şu adrese gönder:", "youre_sending_x_to" : "Bu adrese {receivedBalance} adet coin yolluyorsunuz;", "youre_sending_x_in_y_txs_to" : "Bu adrese {receivedBalance} adet coini {receivedTxCount} adet transferle yolluyorsunuz ", "receiver_address" : "Alıcı Adresi", "wallet_password" : "Cüzdan Şifresi", "send" : "Gönder", "coins_sent" : "Coinleriniz gönderildi!", "view_trans_plural" : "Transferleri görüntüle", "view_trans_singular" : "Transferi görüntüle", "failed_to_send" : "Coinleriniz gönderilemedi", "password_required" : "Cüzdan şifresini giriniz.", "invalid_address" : "Geçersiz adres.", "script_failure" : "Script başarısız oldu.", "could_not_calculate_fee" : "Komisyon hesaplanamadı", "insufficient_funds" : "Yetersiz bakiye", "sign_failed" : "Transfer imzalanamadı. Şifreyi kontrol ediniz.", "send_failed" : "Transfer yapılamadı. Daha fazla bilgi için debug.log inceleyiniz." }, "settings" : { "enable_debug" : "Debugging'i aktif et", "enable_debug_sub" : "Madenci konsolunun çıktılarını debug.log'a dahil et. Log boyutu çok büyüyebilir", "auto_start" : "Otomatik başlat", "auto_start_sub" : "Madenciyi bilgisayar açıldığında otomatik başlat", "closed_source" : "Kapalı kaynak kodlu madenci yazılımlarını kullan", "closed_source_sub" : "Daha iyi hashrate alırsınız, fakat yazılımın geliştiricisi sizden komisyon alır", "closed_source_warning" : "Kapalı kaynak kodlu madenci yazılımı kullanmayı seçtiniz. Vertcoin bu yazılımları desteklemez. İçerikleri bilinemez ve bilgisayarınıza zarar verebilecek yazılımlar içerebilir.", "testnet":"Testnet modu", "testnet_sub":"Testnet modunu aktifleştirir. Kazılan coinler gerçek değildir.", "skipverthashverify":"Başlangıçta Verthash'i doğrulama", "skipverthashverify_sub":"Başlangıçta Verthash dosyasının bütünlüğünü doğrulamayı atlayacak (önerilmez)", "save_n_restart" : "Kaydet ve Yeniden Başlat", "pool": "Madenci havuzu" }, "tabbar" : { "wallet" : "Cüzdan", "send_coins" : "Coin gönder", "settings" : "Ayarlar" }, "tracking": { "update_available" : "Yeni bir güncelleme var.", "tracking_enabled" : "Anonim olarak kullanıcı istatistiği gönderiyorsunuz", "disable_tracking" : "Deaktive et", "tracking_disabled" : "Kullanım istatistiği yollamıyorsunuz", "enable_tracking" : "Kullanıcı deneyimini geliştirmemize yardımcı olmak için aktif edin", "report_issue" : "Sorun bildir" }, "update" : { "new_version_available" : "Yeni bir versiyon mevcut", "download" : "İndir" } } ================================================ FILE: frontend/src/i18n/zh.json ================================================ { "generic" : { "retry" : "再试一次", "back_to_wallet" : "返回钱包", "close" : "关闭" }, "welcome" : { "alreadyrunning" : "一键式矿工《One-Click Miner》 已经在运行。你不能多次运行它。", "click_button_to_start" : "点击下面的按钮再次开始挖矿。", "startmining" : "开始挖矿!", "makeapassword" : "请输入密码。不要丢失你的密码。", "password" : "密码", "confirmpassword" : "确认密码", "password_cannot_be_empty" : "请输入密码", "password_mismatch" : "密码不一致", "error_initializing" : "初始化中发生了故障" }, "checks" : { "prerequisite" : "先行程序已运行,你可能看到弹窗弹出询问权限(可能只在任务栏中闪动)", "checks_failed" : "检查失败", "checking_mining_software" : "检查挖矿软件。。。", "rapidfail" : "检查是否发生快速失败", "compatibility" : "检查GPU兼容性。。。", "installing_miners" : "安装挖矿软件。。。" }, "mining" : { "spendable_balance" : "可花费余额", "still_maturing" : "还在成熟中", "pending_pool_payout" : "等待矿池支出", "waiting_for_miners" : "等待矿工开始", "expected_earnings_24h" : "预期收益(24小时)", "estimating" : "估计中", "stop_mining" : "停止挖矿" }, "sending" : { "send_all_to" : "发送你所有挖矿的币到", "youre_sending_x_to" : "你正在发送 {receivedBalance} 给", "youre_sending_x_in_y_txs_to" : "你将在 {receivedTxCount} 的交易中发送 {receivedBalance}", "receiver_address" : "接收地址", "wallet_password" : "钱包密码", "send" : "发送", "coins_sent" : "你的币被发送了!", "view_trans_plural" : "查看交易信息", "view_trans_singular" : "查看交易信息", "failed_to_send" : "发送币失败了", "password_required" : "请输入钱包密码", "invalid_address" : "无效地址", "script_failure" : "脚本失败", "could_not_calculate_fee" : "无法计算费用", "insufficient_funds" : "不足资金", "sign_failed":"无法签署交易。 检查密码", "send_failed" : "无法发送交易。查看debug.log以获取更多的信息" }, "settings" : { "enable_debug" : "启用调试", "enable_debug_sub" : "在调试日志中包含矿工的控制台输出。可以使你的日志变得很大。", "auto_start" : "自动开始", "auto_start_sub" : "登陆电脑时启动一键式矿工《One-Click Miner》", "closed_source" : "使用闭源的矿工", "closed_source_sub" : "更好的哈希率,但是未经审核的矿工会产生开发人员的费用", "closed_source_warning" : "你选择使用闭源的矿工。绿币(VTC)不支持这些矿工。他们无法对其内容进行审核并且可能包含损害你的电脑的功能。", "save_n_restart" : "保存及重启" }, "tabbar" : { "wallet" : "钱包", "send_coins" : "发送币", "settings" : "设置" }, "tracking": { "update_available" : "更新可用", "tracking_enabled" : "你是匿名共享使用情况统计信息", "disable_tracking" : "停用", "tracking_disabled" : "你没有共享使用情况统计信息", "enable_tracking" : "启用这些可以帮我们改善你的应用体验", "report_issue" : "报告问题" }, "update" : { "new_version_available" : "新版本可用", "download" : "下载" } } ================================================ FILE: frontend/src/main.js ================================================ import Vue from "vue"; import App from "./App.vue"; import VueI18n from "vue-i18n"; import * as Wails from "@wailsapp/runtime" Vue.use(VueI18n); Vue.config.productionTip = false; Vue.config.devtools = true; // Import all locales import locale_bg from "./i18n/bg.json"; import locale_da from "./i18n/da.json"; import locale_de from "./i18n/de.json"; import locale_en from "./i18n/en.json"; import locale_es from "./i18n/es.json"; import locale_fr from "./i18n/fr.json"; import locale_hi from "./i18n/hi.json"; import locale_hr from "./i18n/hr.json"; import locale_it from "./i18n/it.json"; import locale_ja from "./i18n/ja.json"; import locale_lt from "./i18n/lt.json"; import locale_nl from "./i18n/nl.json"; import locale_no from "./i18n/no.json"; import locale_pa from "./i18n/pa.json"; import locale_pl from "./i18n/pl.json"; import locale_pt from "./i18n/pt.json"; import locale_ro from "./i18n/ro.json"; import locale_ru from "./i18n/ru.json"; import locale_sl from "./i18n/sl.json"; import locale_sv from "./i18n/sv.json"; import locale_tr from "./i18n/tr.json"; import locale_zh from "./i18n/zh.json"; Wails.Init(() => { window.backend.Backend.GetLocale().then(result => { const i18n = new VueI18n({ locale: result, // set locale fallbackLocale: 'en', messages: { bg: locale_bg, da: locale_da, de: locale_de, en: locale_en, es: locale_es, fr: locale_fr, hi: locale_hi, hr: locale_hr, it: locale_it, ja: locale_ja, lt: locale_lt, nl: locale_nl, no: locale_no, pa: locale_pa, pl: locale_pl, pt: locale_pt, ro: locale_ro, ru: locale_ru, sl: locale_sl, sv: locale_sv, tr: locale_tr, zh: locale_zh, }, }); new Vue({ i18n, render: h => h(App) }).$mount("#app"); }); }); ================================================ FILE: frontend/vue.config.js ================================================ let cssConfig = {}; if (process.env.NODE_ENV == "production") { cssConfig = { extract: { filename: "[name].css", chunkFilename: "[name].css" } }; } module.exports = { chainWebpack: config => { let limit = 9999999999999999; config.module .rule("images") .test(/\.(png|gif|jpg)(\?.*)?$/i) .use("url-loader") .loader("url-loader") .tap(options => Object.assign(options, { limit: limit })); config.module .rule("fonts") .test(/\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/i) .use("url-loader") .loader("url-loader") .options({ limit: limit }); }, css: cssConfig, configureWebpack: { output: { filename: "[name].js" }, optimization: { splitChunks: false } }, devServer: { disableHostCheck: true, host: "localhost" } }; ================================================ FILE: go.mod ================================================ module github.com/vertcoin-project/one-click-miner-vnext go 1.22 require ( github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/btcsuite/btcd/btcutil v1.1.5 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/btcsuite/fastsha256 v0.0.0-20160815193821-637e65642941 github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 github.com/gertjaap/verthash-go v0.0.0-20210205201258-234a3a9698d1 github.com/go-ping/ping v1.1.0 github.com/marcsauter/single v0.0.0-20201009143647-9f8d81240be2 github.com/tidwall/buntdb v1.3.0 github.com/wailsapp/wails v1.16.9 golang.org/x/crypto v0.20.0 golang.org/x/text v0.14.0 ) require ( github.com/Masterminds/semver v1.5.0 // indirect github.com/abadojack/whatlanggo v1.0.1 // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/fatih/color v1.16.0 // indirect github.com/go-playground/colors v1.2.0 // indirect github.com/google/uuid v1.2.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/jackmordaunt/icns v1.0.0 // indirect github.com/kennygrant/sanitize v1.2.4 // indirect github.com/leaanthony/slicer v1.6.0 // indirect github.com/leaanthony/spinner v0.5.3 // indirect github.com/leaanthony/synx v0.1.0 // indirect github.com/leaanthony/wincursor v0.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba // indirect github.com/tidwall/btree v1.7.0 // indirect github.com/tidwall/gjson v1.17.1 // indirect github.com/tidwall/grect v0.1.4 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/tidwall/rtred v0.1.2 // indirect github.com/tidwall/tinyqueue v0.1.1 // indirect golang.org/x/image v0.15.0 // indirect golang.org/x/net v0.21.0 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) ================================================ FILE: go.sum ================================================ github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:D+aZah+k14Gn6kmL7eKxoo/4Dr/lK3ChBcwce2+SQP4= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4= github.com/abadojack/whatlanggo v1.0.1 h1:19N6YogDnf71CTHm3Mp2qhYfkRdyvbgwWdd2EPxJRG4= github.com/abadojack/whatlanggo v1.0.1/go.mod h1:66WiQbSbJBIlOZMsvbKe5m6pzQovxCH9B/K8tQB2uoc= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd h1:js1gPwhcFflTZ7Nzl7WHaOTlTr5hIrR4n1NM4v9n4Kw= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/fastsha256 v0.0.0-20160815193821-637e65642941 h1:kij1x2aL7VE6gtx8KMIt8PGPgI5GV9LgtHFG5KaEMPY= github.com/btcsuite/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:QcFA8DZHtuIAdYKCq/BzELOaznRsCvwf4zTPmaYwaig= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do= github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gertjaap/verthash-go v0.0.0-20210205201258-234a3a9698d1 h1:vSHBMfat507JbiOZdNICsiCUmoydc3oLzkOTIb/s+DY= github.com/gertjaap/verthash-go v0.0.0-20210205201258-234a3a9698d1/go.mod h1:YNFDXNhMlbkK6pJhNpLWgXWgsU+ISV8AWGdLC7RFlho= github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw= github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk= github.com/go-playground/colors v1.2.0 h1:0EdjTXKrr2g1L/LQTYtIqabeHpZuGZz1U4osS1T8+5M= github.com/go-playground/colors v1.2.0/go.mod h1:miw1R2JIE19cclPxsXqNdzLZsk4DP4iF+m88bRc7kfM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jackmordaunt/icns v1.0.0 h1:RYSxplerf/l/DUd09AHtITwckkv/mqjVv4DjYdPmAMQ= github.com/jackmordaunt/icns v1.0.0/go.mod h1:7TTQVEuGzVVfOPPlLNHJIkzA6CoV7aH1Dv9dW351oOo= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o= github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/leaanthony/slicer v1.4.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js= github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8= github.com/leaanthony/spinner v0.5.3 h1:IMTvgdQCec5QA4qRy0wil4XsRP+QcG1OwLWVK/LPZ5Y= github.com/leaanthony/spinner v0.5.3/go.mod h1:oHlrvWicr++CVV7ALWYi+qHk/XNA91D9IJ48IqmpVUo= github.com/leaanthony/synx v0.1.0 h1:R0lmg2w6VMb8XcotOwAe5DLyzwjLrskNkwU7LLWsyL8= github.com/leaanthony/synx v0.1.0/go.mod h1:Iz7eybeeG8bdq640iR+CwYb8p+9EOsgMWghkSRyZcqs= github.com/leaanthony/wincursor v0.1.0 h1:Dsyp68QcF5cCs65AMBmxoYNEm0n8K7mMchG6a8fYxf8= github.com/leaanthony/wincursor v0.1.0/go.mod h1:7TVwwrzSH/2Y9gLOGH+VhA+bZhoWXBRgbGNTMk+yimE= github.com/marcsauter/single v0.0.0-20201009143647-9f8d81240be2 h1:TyUcIW0tpCQzV4Hpe9jF3p590EQFnMQV3sv6DhoxV6Q= github.com/marcsauter/single v0.0.0-20201009143647-9f8d81240be2/go.mod h1:uUA07IN7rYmbr5YlZM5nDVLyoxiqqpprFlXBrjqI24A= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba h1:2DHfQOxcpWdGf5q5IzCUFPNvRX9Icf+09RvQK2VnJq0= github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba/go.mod h1:iLnlXG2Pakcii2CU0cbY07DRCSvpWNa7nFxtevhOChk= github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI= github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8= github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tidwall/buntdb v1.3.0 h1:gdhWO+/YwoB2qZMeAU9JcWWsHSYU3OvcieYgFRS0zwA= github.com/tidwall/buntdb v1.3.0/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg= github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q= github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8= github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8= github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ= github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE= github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= github.com/wailsapp/wails v1.16.9 h1:H0jWILvWU1I3y2MgqwIPjMfD95/nQP9qAuqAsadYaOs= github.com/wailsapp/wails v1.16.9/go.mod h1:R4AAEWp6K4c0nIMHj5jmr+WQ4yXTfzLXbQoXbg2vEHM= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180606202747-9527bec2660b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/AlecAivazis/survey.v1 v1.8.4/go.mod h1:iBNOmqKz/NUbZx3bA+4hAGLRC7fSK7tgtVDT4tB22XA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ================================================ FILE: keyfile/keyfile.go ================================================ package keyfile import ( "bytes" "crypto/rand" "fmt" "os" "path/filepath" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/base58" "github.com/btcsuite/btcd/txscript" "github.com/vertcoin-project/one-click-miner-vnext/logging" "github.com/vertcoin-project/one-click-miner-vnext/networks" "github.com/vertcoin-project/one-click-miner-vnext/util" "golang.org/x/crypto/nacl/secretbox" "golang.org/x/crypto/scrypt" ) // KeyFileValid returns true if there is a valid initialized keyfile // available func KeyFileValid() bool { return len(loadPublicKey()) == 33 } func CreateKeyFile(pass string) error { filename := keyFile() // Create random key priv32 := new([32]byte) _, err := rand.Read(priv32[:]) if err != nil { return err } // Derive pubkey _, pub := btcec.PrivKeyFromBytes(priv32[:]) salt := new([24]byte) // salt for scrypt / nonce for secretbox dk32 := new([32]byte) // derived key from scrypt //get 24 random bytes for scrypt salt (and secretbox nonce) _, err = rand.Read(salt[:]) if err != nil { return err } // next use the pass and salt to make a 32-byte derived key dk, err := scrypt.Key([]byte(pass), salt[:], 16384, 8, 1, 32) if err != nil { return err } copy(dk32[:], dk[:]) enckey := append(salt[:], secretbox.Seal(nil, priv32[:], salt, dk32)...) return os.WriteFile(filename, append(pub.SerializeCompressed(), enckey...), 0600) } func keyFile() string { return filepath.Join(util.DataDirectory(), "keyfile.hex") } func loadPublicKey() []byte { filename := keyFile() b, err := os.ReadFile(filename) if err != nil { logging.Infof("Error reading keyfile: %s", err.Error()) return []byte{} } if len(b) != 105 { logging.Infof("Keyfile had wrong length. Expected 129, got %d", len(b)) return []byte{} } ret := make([]byte, 33) copy(ret, b[:33]) b = nil return ret } func GetPublicKey() []byte { pub := loadPublicKey() return pub } func GetAddress() string { pub := loadPublicKey() return base58.CheckEncode(btcutil.Hash160(pub), networks.Active.Base58P2PKHVersion) } func GetScript() ([]byte, error) { pub := loadPublicKey() return txscript.NewScriptBuilder().AddOp(txscript.OP_DUP). AddOp(txscript.OP_HASH160).AddData(btcutil.Hash160(pub)). AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).Script() } func LoadPrivateKey(password string) ([]byte, error) { filename := keyFile() keyfile, err := os.ReadFile(filename) if err != nil { return []byte{}, err } if len(keyfile) != 105 { return []byte{}, fmt.Errorf("Key length error for %s ", filename) } enckey := keyfile[33:] // enckey is actually encrypted, get derived key from pass and salt // first extract salt salt := new([24]byte) // salt (also nonce for secretbox) dk32 := new([32]byte) // derived key array copy(salt[:], enckey[:24]) // first 24 bytes are scrypt salt/box nonce dk, err := scrypt.Key([]byte(password), salt[:], 16384, 8, 1, 32) // derive key if err != nil { return []byte{}, err } copy(dk32[:], dk[:]) // copy into fixed size array // nonce for secretbox is the same as scrypt salt. Seems fine. Really. priv, worked := secretbox.Open(nil, enckey[24:], salt, dk32) if !worked { return []byte{}, fmt.Errorf("Decryption failed for %s ", filename) } return priv, nil } func TestPassword(password string) bool { priv, err := LoadPrivateKey(password) if err != nil { return false } _, pub := btcec.PrivKeyFromBytes(priv) return bytes.Equal(loadPublicKey(), pub.SerializeCompressed()) } ================================================ FILE: logging/log.go ================================================ package logging // Log Levels: // 3: DebugLevel prints Panics, Fatals, Errors, Warnings, Infos and Debugs // 2: InfoLevel prints Panics, Fatals, Errors, Warnings and Info // 1: WarnLevel prints Panics, Fatals, Errors and Warnings // 0: ErrorLevel prints Panics, Fatals and Errors // Default is level 0 // Code for tagging logs: // Debug -> Useful debugging information // Info -> Something noteworthy happened // Warn -> You should probably take a look at this // Error -> Something failed but I'm not quitting // Fatal -> Bye import ( "fmt" "io" "log" "os" ) type LogLevel int const ( LogLevelError LogLevel = 0 LogLevelWarning LogLevel = 1 LogLevelInfo LogLevel = 2 LogLevelDebug LogLevel = 3 ) var logLevel = LogLevelError // the default func SetLogLevel(newLevel int) { logLevel = LogLevel(newLevel) } func SetLogFile(logFile io.Writer) { log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds) _, err := os.Stdout.Write([]byte("\n")) if err == nil { log.SetOutput(io.MultiWriter(os.Stdout, logFile)) } else { log.SetOutput(logFile) } } func getPrefix(level string) string { return fmt.Sprintf("[%s]", level) } func Fatalln(args ...interface{}) { log.Fatalln(args...) } func Fatalf(format string, args ...interface{}) { log.Fatalf(format, args...) } func Fatal(args ...interface{}) { log.Fatal(args...) } func Debugf(format string, args ...interface{}) { if logLevel >= LogLevelDebug { log.Printf(fmt.Sprintf("%s %s", getPrefix("DEBUG"), format), args...) } } func Infof(format string, args ...interface{}) { if logLevel >= LogLevelInfo { log.Printf(fmt.Sprintf("%s %s", getPrefix("INFO"), format), args...) } } func Warnf(format string, args ...interface{}) { if logLevel >= LogLevelWarning { log.Printf(fmt.Sprintf("%s %s", getPrefix("WARN"), format), args...) } } func Errorf(format string, args ...interface{}) { if logLevel >= LogLevelError { log.Printf(fmt.Sprintf("%s %s", getPrefix("ERROR"), format), args...) } } func Debugln(args ...interface{}) { if logLevel >= LogLevelDebug { args = append([]interface{}{getPrefix("DEBUG")}, args...) log.Println(args...) } } func Infoln(args ...interface{}) { if logLevel >= LogLevelInfo { args = append([]interface{}{getPrefix("INFO")}, args...) log.Println(args...) } } func Warnln(args ...interface{}) { if logLevel >= LogLevelWarning { args = append([]interface{}{getPrefix("WARN")}, args...) log.Println(args...) } } func Errorln(args ...interface{}) { if logLevel >= LogLevelError { args = append([]interface{}{getPrefix("ERROR")}, args...) log.Println(args...) } } func Debug(args ...interface{}) { if logLevel >= LogLevelDebug { args = append([]interface{}{getPrefix("DEBUG")}, args...) log.Print(args...) } } func Info(args ...interface{}) { if logLevel >= LogLevelInfo { args = append([]interface{}{getPrefix("INFO")}, args...) log.Print(args...) } } func Warn(args ...interface{}) { if logLevel >= LogLevelWarning { args = append([]interface{}{getPrefix("WARN")}, args...) log.Print(args...) } } func Error(args ...interface{}) { if logLevel >= LogLevelError { args = append([]interface{}{getPrefix("ERROR")}, args...) log.Print(args...) } } ================================================ FILE: main.go ================================================ package main import ( "fmt" "log" "os" "path/filepath" "runtime/debug" _ "embed" "github.com/marcsauter/single" "github.com/vertcoin-project/one-click-miner-vnext/backend" "github.com/vertcoin-project/one-click-miner-vnext/logging" "github.com/vertcoin-project/one-click-miner-vnext/networks" "github.com/vertcoin-project/one-click-miner-vnext/tracking" "github.com/vertcoin-project/one-click-miner-vnext/util" "github.com/wailsapp/wails" ) //nolint:all //go:embed frontend/dist/app.js var js string //nolint:all //go:embed frontend/dist/app.css var css string func main() { defer func() { if err := recover(); err != nil { // Reopen log file, since it's closed now! logging.SetLogLevel(int(logging.LogLevelDebug)) logFilePath := filepath.Join(util.DataDirectory(), "debug.log") logFile, _ := os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) logging.SetLogFile(logFile) defer logFile.Close() logging.Errorf("%v\n%s\n", err, string(debug.Stack())) tracking.Track(tracking.TrackingRequest{ Category: "Lifecycle", Action: "Crash", Name: fmt.Sprintf("%v", err), }) } }() tracking.StartTracker() tracking.Track(tracking.TrackingRequest{ Category: "Lifecycle", Action: "Startup", Name: fmt.Sprintf("OCM/%s", tracking.GetVersion()), }) logging.SetLogLevel(int(logging.LogLevelDebug)) if _, err := os.Stat(util.DataDirectory()); os.IsNotExist(err) { logging.Infof("Creating data directory") err = os.MkdirAll(util.DataDirectory(), 0700) if err != nil && !os.IsExist(err) { logging.Errorf("Error creating data directory, cannot continue") os.Exit(1) } } logFilePath := filepath.Join(util.DataDirectory(), "debug.log") logFile, _ := os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) logging.SetLogFile(logFile) defer logFile.Close() log.Printf("OCM v%s Started up\n", tracking.GetVersion()) app := wails.CreateApp(&wails.AppConfig{ Width: 800, Height: 400, Title: "Vertcoin One Click Miner", JS: js, CSS: css, Colour: "#131313", }) alreadyRunning := false s := single.New("vertcoin-ocm") if err := s.CheckLock(); err != nil && err == single.ErrAlreadyRunning { alreadyRunning = true } else if err == nil { defer func() { err := s.TryUnlock() if err != nil { logging.Errorf("Error unlocking OCM: %v", err) } }() } backend, err := backend.NewBackend(alreadyRunning) if err != nil { logging.Errorf("Error creating Backend: %s", err.Error()) panic(err) } networks.SetNetwork(backend.GetTestnet()) go backend.BackendServerSelector() go backend.SelectP2PoolNode() backend.ResetPool() app.Bind(backend) err = app.Run() if err != nil { logging.Errorf("Error running app: %v", err) } backend.StopMining() tracking.Track(tracking.TrackingRequest{ Category: "Lifecycle", Action: "Shutdown", }) tracking.Stop() } ================================================ FILE: miners/ccminer.go ================================================ package miners import ( "strconv" "strings" "sync" "github.com/vertcoin-project/one-click-miner-vnext/logging" ) // Compile time assertion on interface var _ MinerImpl = &CCMinerImpl{} type CCMinerImpl struct { binaryRunner *BinaryRunner hashRates map[int64]uint64 hashRatesLock sync.Mutex gpuCount int8 } func NewCCMinerImpl(br *BinaryRunner) MinerImpl { return &CCMinerImpl{binaryRunner: br, hashRates: map[int64]uint64{}, hashRatesLock: sync.Mutex{}} } func (l *CCMinerImpl) Configure(args BinaryArguments) error { return nil } func (l *CCMinerImpl) ParseOutput(line string) { if l.binaryRunner.Debug { logging.Debugf("[ccminer] %s\n", line) } line = strings.TrimSpace(line) if strings.Contains(line, "GPU #") && strings.HasSuffix(line, ")") { startCountIdx := strings.Index(line, "GPU #") + 5 gpuCountString := line[startCountIdx : startCountIdx+1] gpuCount64, _ := strconv.ParseInt(gpuCountString, 10, 8) l.gpuCount = int8(gpuCount64) + 1 logging.Debugf("Set GPU Count to %d", l.gpuCount) } if strings.Contains(line, "GPU #") && strings.HasSuffix(line, "H/s") { startDeviceIdx := strings.Index(line, "GPU #") endDeviceIdx := strings.Index(line[startDeviceIdx:], ":") deviceIdxString := line[startDeviceIdx+5 : startDeviceIdx+endDeviceIdx] deviceIdx, err := strconv.ParseInt(deviceIdxString, 10, 64) if err != nil { return } startMHs := strings.LastIndex(line, ", ") if startMHs > -1 { hashRateUnit := strings.ToUpper(line[len(line)-4 : len(line)-3]) line = line[startMHs+2 : len(line)-5] f, err := strconv.ParseFloat(line, 64) if err != nil { logging.Errorf("Error parsing hashrate: %s\n", err.Error()) } if hashRateUnit == "K" { f = f * 1000 } else if hashRateUnit == "M" { f = f * 1000 * 1000 } else if hashRateUnit == "G" { f = f * 1000 * 1000 * 1000 } l.hashRatesLock.Lock() l.hashRates[deviceIdx] = uint64(f) l.hashRatesLock.Unlock() } } } func (l *CCMinerImpl) HashRate() uint64 { totalHash := uint64(0) l.hashRatesLock.Lock() for _, h := range l.hashRates { totalHash += h } l.hashRatesLock.Unlock() return totalHash } func (l *CCMinerImpl) ConstructCommandlineArgs(args BinaryArguments) []string { return []string{"--max-log-rate", "0", "--no-color", "-a", "lyra2v3", "-o", args.StratumUrl, "-u", args.StratumUsername, "-p", args.StratumPassword} } func (l *CCMinerImpl) AvailableGPUs() int8 { err := l.binaryRunner.launch([]string{"-n"}, false) if err != nil { return 0 } err = l.binaryRunner.cmd.Wait() if err != nil { return 0 } // Output is caught by ParseOuput function above and this will set the gpuCount accordingly return l.gpuCount } ================================================ FILE: miners/cryptodredge.go ================================================ package miners import ( "strconv" "strings" "sync" "time" "github.com/vertcoin-project/one-click-miner-vnext/logging" ) // Compile time assertion on interface var _ MinerImpl = &CryptoDredgeMinerImpl{} type CryptoDredgeMinerImpl struct { binaryRunner *BinaryRunner hashRates map[int64]uint64 hashRatesLock sync.Mutex gpuCount int8 } func NewCryptoDredgeMinerImpl(br *BinaryRunner) MinerImpl { return &CryptoDredgeMinerImpl{binaryRunner: br, hashRates: map[int64]uint64{}, hashRatesLock: sync.Mutex{}} } func (l *CryptoDredgeMinerImpl) Configure(args BinaryArguments) error { return nil } func (l *CryptoDredgeMinerImpl) ParseOutput(line string) { if l.binaryRunner.Debug { logging.Debugf("[cryptodredge] %s\n", line) } line = strings.TrimSpace(line) if strings.Contains(line, "INFO - GPU") && strings.Contains(line, "MB") { startCountIdx := strings.Index(line, "INFO - GPU") + 11 gpuCountString := line[startCountIdx : startCountIdx+1] gpuCount64, _ := strconv.ParseInt(gpuCountString, 10, 8) l.gpuCount = int8(gpuCount64) + 1 logging.Debugf("Set GPU Count to %d", l.gpuCount) } if strings.Contains(line, "INFO - GPU") && strings.Contains(line, "H/s") { startDeviceIdx := strings.Index(line, "INFO - GPU") endDeviceIdx := strings.Index(line[startDeviceIdx+9:], " ") deviceIdxString := line[startDeviceIdx+11 : startDeviceIdx+9+endDeviceIdx] deviceIdx, err := strconv.ParseInt(deviceIdxString, 10, 64) if err != nil { return } endMHs := strings.Index(line, "H/s") if endMHs > -1 { hashRateUnit := strings.ToUpper(line[endMHs-1 : endMHs]) line = line[:endMHs-1] line = line[strings.LastIndex(line, " ")+1:] line = strings.ReplaceAll(line, ",", ".") f, err := strconv.ParseFloat(line, 64) if err != nil { logging.Errorf("Error parsing hashrate: %s\n", err.Error()) } if hashRateUnit == "K" { f = f * 1000 } else if hashRateUnit == "M" { f = f * 1000 * 1000 } else if hashRateUnit == "G" { f = f * 1000 * 1000 * 1000 } l.hashRatesLock.Lock() l.hashRates[deviceIdx] = uint64(f) l.hashRatesLock.Unlock() } } } func (l *CryptoDredgeMinerImpl) HashRate() uint64 { totalHash := uint64(0) l.hashRatesLock.Lock() for _, h := range l.hashRates { totalHash += h } l.hashRatesLock.Unlock() return totalHash } func (l *CryptoDredgeMinerImpl) ConstructCommandlineArgs(args BinaryArguments) []string { return []string{"--intensity", "5", "--no-color", "-a", "lyra2v3", "-o", args.StratumUrl, "-u", args.StratumUsername, "-p", args.StratumPassword} } func (l *CryptoDredgeMinerImpl) AvailableGPUs() int8 { err := l.binaryRunner.launch([]string{}, false) if err != nil { return 0 } time.Sleep(time.Second) err = l.binaryRunner.Stop() if err != nil { return 0 } // Output is caught by ParseOuput function above and this will set the gpuCount accordingly return l.gpuCount } ================================================ FILE: miners/lyclminer.go ================================================ package miners import ( "bufio" "fmt" "os" "path/filepath" "strconv" "strings" "sync" "github.com/vertcoin-project/one-click-miner-vnext/logging" "github.com/vertcoin-project/one-click-miner-vnext/util" ) // Compile time assertion on interface var _ MinerImpl = &LyclMinerImpl{} type LyclMinerImpl struct { binaryRunner *BinaryRunner hashRates map[int64]uint64 hashRatesLock sync.Mutex } func NewLyclMinerImpl(br *BinaryRunner) MinerImpl { return &LyclMinerImpl{binaryRunner: br, hashRates: map[int64]uint64{}, hashRatesLock: sync.Mutex{}} } func (l *LyclMinerImpl) Configure(args BinaryArguments) error { os.Remove(filepath.Join(util.DataDirectory(), "lyclMiner_tmpl.conf")) err := l.binaryRunner.launch([]string{"-g", filepath.Join(util.DataDirectory(), "lyclMiner_tmpl.conf")}, false) err2 := l.binaryRunner.cmd.Wait() if err != nil { return err } if err2 != nil { return err2 } if !l.binaryRunner.cmd.ProcessState.Success() { return fmt.Errorf("Was unable to configure lyclMiner. Exit code %d", l.binaryRunner.cmd.ProcessState.ExitCode()) } in, err := os.Open(filepath.Join(util.DataDirectory(), "lyclMiner_tmpl.conf")) if err != nil { logging.Error(err) return err } defer in.Close() os.Remove(filepath.Join(util.DataDirectory(), "lyclMiner.conf")) out, err := os.Create(filepath.Join(util.DataDirectory(), "lyclMiner.conf")) if err != nil { logging.Error(err) return err } defer func() { err := out.Close() if err != nil { logging.Error(err) } }() scanner := bufio.NewScanner(in) skip := false for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "#") { skip = false } if strings.HasPrefix(line, "\n\n", args.StratumUrl, args.StratumUsername, args.StratumPassword)) if err != nil { return err } skip = true } if !skip { _, err = out.WriteString(fmt.Sprintf("%s\n", line)) if err != nil { return err } } } if err := scanner.Err(); err != nil { return err } return nil } func (l *LyclMinerImpl) ParseOutput(line string) { if l.binaryRunner.Debug { logging.Debugf("[lyclMiner] %s\n", line) } line = strings.TrimSpace(line) if strings.Contains(line, "Device #") && strings.HasSuffix(line, "H/s") { startDeviceIdx := strings.Index(line, "Device #") endDeviceIdx := strings.Index(line[startDeviceIdx:], ":") deviceIdxString := line[startDeviceIdx+8 : startDeviceIdx+endDeviceIdx] deviceIdx, err := strconv.ParseInt(deviceIdxString, 10, 64) if err != nil { return } startMHs := strings.LastIndex(line, ", ") if startMHs > -1 { hashRateUnit := strings.ToUpper(line[len(line)-4 : len(line)-3]) line = line[startMHs+2 : len(line)-5] f, err := strconv.ParseFloat(line, 64) if err != nil { logging.Errorf("Error parsing hashrate: %s\n", err.Error()) } if hashRateUnit == "K" { f = f * 1000 } else if hashRateUnit == "M" { f = f * 1000 * 1000 } else if hashRateUnit == "G" { f = f * 1000 * 1000 * 1000 } l.hashRatesLock.Lock() l.hashRates[deviceIdx] = uint64(f) l.hashRatesLock.Unlock() } } } func (l *LyclMinerImpl) HashRate() uint64 { totalHash := uint64(0) l.hashRatesLock.Lock() for _, h := range l.hashRates { totalHash += h } l.hashRatesLock.Unlock() return totalHash } func (l *LyclMinerImpl) ConstructCommandlineArgs(args BinaryArguments) []string { return []string{filepath.Join(util.DataDirectory(), "lyclMiner.conf")} } func (l *LyclMinerImpl) AvailableGPUs() int8 { tmpCfg := filepath.Join(util.DataDirectory(), "lyclMiner_tmp.conf") err := l.binaryRunner.launch([]string{"-g", tmpCfg}, false) err2 := l.binaryRunner.cmd.Wait() if err != nil { return 0 } if err2 != nil { return 0 } if !l.binaryRunner.cmd.ProcessState.Success() { return 0 } in, err := os.Open(filepath.Join(util.DataDirectory(), "lyclMiner_tmpl.conf")) if err != nil { logging.Error(err) return 0 } gpu := int8(0) scanner := bufio.NewScanner(in) for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, " 3 { logging.Infof("Miner [%s] is rapidly failing, not restarting it.", b.MinerBinary.MainExecutableName) return RunningStateRapidFail } } logging.Infof("Restarting miner [%s]", b.MinerBinary.MainExecutableName) err := b.restart() if err != nil { logging.Errorf("Error restarting miner: %v", err) } return RunningStateRestarting } return RunningStateRunning } func (b *BinaryRunner) HashRate() uint64 { return b.MinerImpl.HashRate() } func (b *BinaryRunner) restart() error { b.lastStarted = time.Now() return b.launch(b.MinerImpl.ConstructCommandlineArgs(b.usedArgs), true) } func (b *BinaryRunner) Start(args BinaryArguments) error { err := b.Install() if err != nil { return err } b.usedArgs = args b.lastStarted = time.Now() b.rapidFails = 0 // Check if there is a compatible GPU! if b.MinerImpl.AvailableGPUs() == 0 { return fmt.Errorf("err_no_gpus") } // Always do a fresh unpack of the executable to ensure there's been no funny // business. EnsureAvailable already checked the SHA hash. err = b.launch(b.MinerImpl.ConstructCommandlineArgs(args), true) if err != nil { return err } return nil } func (b *BinaryRunner) unpackDir() string { return filepath.Join(util.DataDirectory(), "miners", fmt.Sprintf("unpacked-%s", b.MinerBinary.Hash)) } func (b *BinaryRunner) downloadPath() string { return filepath.Join(util.DataDirectory(), "miners", b.MinerBinary.Hash) } func (b *BinaryRunner) launch(params []string, wait bool) error { exePath := b.findExecutable() if exePath == "" { return fmt.Errorf("Cannot find main miner binary in unpack folder") } logging.Debugf("Launching %s %v\n", exePath, params) b.cmd = exec.Command(exePath, params...) util.PrepareBackgroundCommand(b.cmd) b.cmd.Dir = filepath.Dir(exePath) r, w := io.Pipe() go func(b *BinaryRunner, rd io.Reader) { br := bufio.NewReader(rd) for { l, _, e := br.ReadLine() if e != nil { logging.Debugf("%sError on readline from stdout/err: %s", b.logPrefix(), e.Error()) return } b.MinerImpl.ParseOutput(string(l)) } }(b, r) b.cmd.Stderr = w b.cmd.Stdout = w err := b.cmd.Start() if err != nil { return err } if wait { b.running = true go func() { err := b.cmd.Wait() if err != nil { logging.Errorf("Error in miner: %v", err) } b.running = false }() } return nil } func (b *BinaryRunner) unpack() error { unpackDir := b.unpackDir() if _, err := os.Stat(unpackDir); !os.IsNotExist(err) { logging.Debugf("%sRemoving unpack directory", b.logPrefix()) time.Sleep(1 * time.Second) // Necessary on Windows to avoid permission error err = os.RemoveAll(unpackDir) if err != nil { return err } } if _, err := os.Stat(unpackDir); os.IsNotExist(err) { logging.Debugf("%s(Re)creating unpack directory", b.logPrefix()) err = os.MkdirAll(unpackDir, 0755) if err != nil { return err } } archive := b.downloadPath() if strings.HasSuffix(b.MinerBinary.Url, ".zip") { return util.UnpackZip(archive, unpackDir) } else if strings.HasSuffix(b.MinerBinary.Url, ".tar.gz") || strings.HasSuffix(b.MinerBinary.Url, ".tgz") { return util.UnpackTar(archive, unpackDir) } return fmt.Errorf("Unknown archive format, cannot unpack: %s", b.MinerBinary.Url) } func (b *BinaryRunner) findExecutable() string { mainExecutablePath := "" err := filepath.Walk(b.unpackDir(), func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.Name() == b.MinerBinary.MainExecutableName { mainExecutablePath = path } return nil }) if err != nil { logging.Errorf("Error finding executable: %v", err) } return mainExecutablePath } func (b *BinaryRunner) ensureAvailable() error { freshDownload := false _ = os.Mkdir(filepath.Join(util.DataDirectory(), "miners"), 0700) nodePath := b.downloadPath() _, err := os.Stat(nodePath) if os.IsNotExist(err) { logging.Debugf("%sBinary not found, downloading...", b.logPrefix()) freshDownload = true err := b.download() if err != nil { return err } } else if err != nil { return err } else { logging.Debugf("%sDaemon file already exists", b.logPrefix()) } shaSum, err := util.ShaSum(nodePath) if err != nil { return err } expectedHash, _ := hex.DecodeString(b.MinerBinary.Hash) if !bytes.Equal(shaSum, expectedHash) { logging.Warnf("%sHash differs: [%x] vs [%s]", b.logPrefix(), shaSum, b.MinerBinary.Hash) if !freshDownload { err = os.Remove(nodePath) if err != nil { return err } return b.ensureAvailable() } else { err = fmt.Errorf("%sFreshly downloaded node did not have correct SHA256 hash", b.logPrefix()) logging.Error(err) return err } } logging.Debugf("%sDaemon file is available and correct", b.logPrefix()) return nil } func (b *BinaryRunner) download() error { nodePath := b.downloadPath() resp, err := http.Get(b.MinerBinary.Url) if err != nil { return err } defer resp.Body.Close() // Create the file out, err := os.Create(nodePath) if err != nil { return err } defer out.Close() // Write the body to file _, err = io.Copy(out, resp.Body) logging.Debugf("%sDaemon file downloaded", b.logPrefix()) return err } ================================================ FILE: miners/teamredminer.go ================================================ package miners import ( "strconv" "strings" "sync" "github.com/vertcoin-project/one-click-miner-vnext/logging" ) // Compile time assertion on interface var _ MinerImpl = &TeamRedMinerImpl{} type TeamRedMinerImpl struct { binaryRunner *BinaryRunner hashRates map[int64]uint64 hashRatesLock sync.Mutex gpuCount int8 } func NewTeamRedMinerImpl(br *BinaryRunner) MinerImpl { return &TeamRedMinerImpl{binaryRunner: br, hashRates: map[int64]uint64{}, hashRatesLock: sync.Mutex{}} } func (l *TeamRedMinerImpl) Configure(args BinaryArguments) error { return nil } func (l *TeamRedMinerImpl) ParseOutput(line string) { if l.binaryRunner.Debug { logging.Debugf("[teamRedMiner] %s\n", line) } line = strings.TrimSpace(line) if strings.Contains(line, "] Detected") && strings.Contains(line, "devices, ") { startCountIdx := strings.Index(line, "] Detected ") + 11 gpuCountString := line[startCountIdx : startCountIdx+1] logging.Debugf("GPUCountString: %s", gpuCountString) gpuCount64, _ := strconv.ParseInt(gpuCountString, 10, 8) l.gpuCount = int8(gpuCount64) logging.Debugf("Set GPU Count to %d", l.gpuCount) } if strings.Contains(line, "] GPU ") && strings.Contains(line, "lyra2rev3") { startDeviceIdx := strings.Index(line, "] GPU ") endDeviceIdx := strings.Index(line[startDeviceIdx:], "[") deviceIdxString := line[startDeviceIdx+6 : startDeviceIdx+endDeviceIdx-1] deviceIdx, err := strconv.ParseInt(deviceIdxString, 10, 64) if err != nil { return } startMHs := strings.Index(line, "lyra2rev3: ") if startMHs > -1 { endMHs := strings.Index(line[startMHs:], "h/s") hashRateUnit := strings.ToUpper(line[startMHs+endMHs-1 : startMHs+endMHs]) line = line[startMHs+11 : startMHs+endMHs-1] f, err := strconv.ParseFloat(line, 64) if err != nil { logging.Errorf("Error parsing hashrate: %s\n", err.Error()) } if hashRateUnit == "K" { f = f * 1000 } else if hashRateUnit == "M" { f = f * 1000 * 1000 } else if hashRateUnit == "G" { f = f * 1000 * 1000 * 1000 } l.hashRatesLock.Lock() l.hashRates[deviceIdx] = uint64(f) l.hashRatesLock.Unlock() } } } func (l *TeamRedMinerImpl) HashRate() uint64 { totalHash := uint64(0) l.hashRatesLock.Lock() for _, h := range l.hashRates { totalHash += h } l.hashRatesLock.Unlock() return totalHash } func (l *TeamRedMinerImpl) ConstructCommandlineArgs(args BinaryArguments) []string { return []string{"--log_interval=10", "--disable_colors", "-a", "lyra2rev3", "-o", args.StratumUrl, "-u", args.StratumUsername, "-p", args.StratumPassword} } func (l *TeamRedMinerImpl) AvailableGPUs() int8 { err := l.binaryRunner.launch([]string{"--list_devices"}, false) if err != nil { return 0 } err = l.binaryRunner.cmd.Wait() if err != nil { return 0 } // Output is caught by ParseOuput function above and this will set the gpuCount accordingly return l.gpuCount } ================================================ FILE: miners/verthashminer.go ================================================ package miners import ( "bufio" "fmt" "os" "path/filepath" "strconv" "strings" "sync" "github.com/vertcoin-project/one-click-miner-vnext/logging" "github.com/vertcoin-project/one-click-miner-vnext/util" ) // Compile time assertion on interface var _ MinerImpl = &VerthashMinerImpl{} var cfgPath = "verthash-miner-tmpl.conf" type VerthashMinerImpl struct { binaryRunner *BinaryRunner clhashRates map[int64]uint64 cuhashRates map[int64]uint64 hashRatesLock sync.Mutex } func (l *VerthashMinerImpl) generateTempConf() error { os.Remove(filepath.Join(util.DataDirectory(), cfgPath)) err := l.binaryRunner.launch([]string{"--gen-conf", filepath.Join(util.DataDirectory(), cfgPath)}, false) var err2 error if l.binaryRunner.cmd != nil { err2 = l.binaryRunner.cmd.Wait() } if err != nil { return err } if err2 != nil { return err2 } return nil } func NewVerthashMinerImpl(br *BinaryRunner) MinerImpl { return &VerthashMinerImpl{binaryRunner: br, clhashRates: map[int64]uint64{}, cuhashRates: map[int64]uint64{}, hashRatesLock: sync.Mutex{}} } func (l *VerthashMinerImpl) Configure(args BinaryArguments) error { err := l.generateTempConf() if err != nil { return err } if !l.binaryRunner.cmd.ProcessState.Success() { return fmt.Errorf("Was unable to configure VerthashMiner. Exit code %d", l.binaryRunner.cmd.ProcessState.ExitCode()) } in, err := os.Open(filepath.Join(util.DataDirectory(), "verthash-miner-tmpl.conf")) if err != nil { logging.Error(err) return err } defer in.Close() os.Remove(filepath.Join(util.DataDirectory(), "verthash-miner.conf")) out, err := os.Create(filepath.Join(util.DataDirectory(), "verthash-miner.conf")) if err != nil { logging.Error(err) return err } defer func() { err := out.Close() if err != nil { logging.Error(err) } }() var parsedDevices map[int]util.VerthashMinerDeviceConfig scanner := bufio.NewScanner(in) skip := false insideDeviceBlock := false deviceBlockStr := "" for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "#") { skip = false } if strings.HasPrefix(line, "\n\n", args.StratumUrl, args.StratumUsername, args.StratumPassword)) if err != nil { return err } skip = true } if strings.HasPrefix(line, "\n\n", filepath.Join(util.DataDirectory(), "verthash.dat"))) if err != nil { return err } skip = true } if strings.Contains(line, "OpenCL device config") || strings.Contains(line, "CUDA Device config") { logging.Debug("Entering device block") insideDeviceBlock = true } else if insideDeviceBlock { deviceBlockStr += line + "\n" } if strings.Contains(line, "#-#-#-#-#-#-#-#-#-#-#-") && insideDeviceBlock { insideDeviceBlock = false parsedDevices = util.ParseVerthashMinerDeviceCfg(deviceBlockStr) logging.Debug("Exiting device block") logging.Debug(parsedDevices[0]) deviceBlockStr = "" } if strings.HasPrefix(line, " -1 { deviceIdxStart := strings.Index(line, "_device(") + 8 deviceTypeStart := strings.Index(line, "_device(") - 2 deviceIdxEnd := strings.Index(line[deviceIdxStart:], ")") deviceIdxString := line[deviceIdxStart : deviceIdxStart+deviceIdxEnd] deviceIdx, _ := strconv.ParseInt(deviceIdxString, 10, 64) deviceType := line[deviceTypeStart : deviceTypeStart+2] hashRateUnit := strings.ToUpper(line[len(line)-4 : len(line)-3]) line = line[startMHs+2 : len(line)-5] f, err := strconv.ParseFloat(line, 64) if err != nil { logging.Errorf("Error parsing hashrate: %s\n", err.Error()) } if hashRateUnit == "K" { f = f * 1000 } else if hashRateUnit == "M" { f = f * 1000 * 1000 } else if hashRateUnit == "G" { f = f * 1000 * 1000 * 1000 } l.hashRatesLock.Lock() if deviceType == "cu" { l.cuhashRates[deviceIdx] = uint64(f) } else { l.clhashRates[deviceIdx] = uint64(f) } l.hashRatesLock.Unlock() } } } func (l *VerthashMinerImpl) HashRate() uint64 { totalHash := uint64(0) l.hashRatesLock.Lock() for _, h := range l.cuhashRates { totalHash += h } for _, h := range l.clhashRates { totalHash += h } l.hashRatesLock.Unlock() return totalHash } func (l *VerthashMinerImpl) ConstructCommandlineArgs(args BinaryArguments) []string { return []string{"--conf", filepath.Join(util.DataDirectory(), "verthash-miner.conf")} } func (l *VerthashMinerImpl) AvailableGPUs() int8 { logging.Debugf("AvailableGPUs called\n") tmpCfg := filepath.Join(util.DataDirectory(), "verthash-miner-tmp.conf") err := l.binaryRunner.launch([]string{"--gen-conf", tmpCfg}, false) err2 := l.binaryRunner.cmd.Wait() if err != nil { logging.Error(err) return 0 } if err2 != nil { logging.Error(err) return 0 } if !l.binaryRunner.cmd.ProcessState.Success() { logging.Errorf("Process state: %d", l.binaryRunner.cmd.ProcessState) return 0 } in, err := os.Open(tmpCfg) if err != nil { logging.Error(err) return 0 } gpu := int8(0) scanner := bufio.NewScanner(in) for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, " Set.MaxNextPing { Selected = SelectedNode{ P2PoolStratum: NodeList[i].Stratum, P2PoolURL: NodeList[i].URL, } logging.Infof("%s selected, next node has too high ping time\n", NodeList[i].Hostname) break } logging.Warnf("%s had more than %v miners, trying new inorder to retain efficiency\n", NodeList[i].Hostname, Set.MaxMiners) } } else { logging.Warnf("%s is either unreachable or had more than a %f fee, trying new\n", NodeList[i].Hostname, Set.MaxFee) } } } } } func PingNodes(NodeList []Nodes) error { var wg sync.WaitGroup var errPing error for i := 0; i < len(NodeList); i++ { wg.Add(1) go func(NodeList []Nodes) { defer wg.Done() pinger, err := ping.NewPinger(NodeList[i].Hostname) pinger.SetPrivileged(true) //This line is needed for windows because of ICMP pinger.Timeout = Set.PingTimeout //Sets the time for which the pinger will timeout regardless of how many packets there has been recieved if err != nil { logging.Warn("Error: Check if you are connected to the internet") logging.Warn(err) errPing = err return } pinger.Count = Set.PingPackets //Number of packets to be sent to each node err = pinger.Run() if err != nil { logging.Warn("Error: Check if you are connected to the internet") logging.Warn(err) errPing = err return } NodeList[i].PingTime = pinger.Statistics().AvgRtt logging.Infof("%s: %v \n", NodeList[i].Hostname, NodeList[i].PingTime) }(NodeList) } wg.Wait() if errPing != nil { return errPing } return nil } // Instead of making a http request to the node each time we need to get information, we do it once and then reuse the collected data. func GetNodeInformation(NodeURL string) (jsonPayload map[string]interface{}, err error) { err = util.GetJson(fmt.Sprintf("%slocal_stats", NodeURL), &jsonPayload) if err != nil { if NodeURL != "http://127.0.0.1:9171/" { logging.Errorf("Unable to fetch node information\n", err.Error()) } return jsonPayload, err } return jsonPayload, nil } func CheckFee(jsonPayload map[string]interface{}) bool { fee, ok := jsonPayload["fee"].(float64) if !ok { return false } donationFee, ok := jsonPayload["donation_proportion"].(float64) if !ok { return false } fee += donationFee return fee <= Set.MaxFee } // To ensure efficiency of the selected p2pool node a limit of miners has been put in place, returns true if the number is equal to Maxminers or below func CheckCurrentMiners(jsonPayload map[string]interface{}) bool { currentMiners, _ := jsonPayload["miner_hash_rates"].(string) return len(currentMiners) <= Set.MaxMiners } ================================================ FILE: pools/miningpoolsweden.go ================================================ package pools import ( "fmt" "time" "github.com/vertcoin-project/one-click-miner-vnext/util" ) var _ Pool = &MiningpoolSweden{} type MiningpoolSweden struct { Address string LastFetchedPayout time.Time LastPayout uint64 } func NewMiningpoolSweden(addr string) *MiningpoolSweden { return &MiningpoolSweden{Address: addr} } func (p *MiningpoolSweden) GetPendingPayout() uint64 { jsonPayload := map[string]interface{}{} err := util.GetJson(fmt.Sprintf("https://api.miningpoolsweden.eu/api/pools/vert1/miners/%s", p.Address), &jsonPayload) if err != nil { return 0 } vtc, ok := jsonPayload["pendingBalance"].(float64) if !ok { return 0 } vtc *= 100000000 return uint64(vtc) } func (p *MiningpoolSweden) GetStratumUrl() string { return "stratum+tcp://vtc.miningpoolsweden.eu:3052" } func (p *MiningpoolSweden) GetUsername() string { return p.Address } func (p *MiningpoolSweden) GetPassword() string { return "x" } func (p *MiningpoolSweden) GetID() int { return 9 } func (p *MiningpoolSweden) GetName() string { return "MiningpoolSweden.eu" } func (p *MiningpoolSweden) GetFee() float64 { return 0.6 } func (p *MiningpoolSweden) OpenBrowserPayoutInfo(addr string) { util.OpenBrowser(fmt.Sprintf("https://miningpoolsweden.eu/?#vert1/dashboard?address=%s", addr)) } ================================================ FILE: pools/p2pool.go ================================================ package pools import ( "fmt" "time" "github.com/vertcoin-project/one-click-miner-vnext/logging" "github.com/vertcoin-project/one-click-miner-vnext/ping" "github.com/vertcoin-project/one-click-miner-vnext/util" ) var _ Pool = &P2Pool{} type P2Pool struct { Address string LastFetchedPayout time.Time LastPayout uint64 LastFetchedFee time.Time LastFee float64 } func NewP2Pool(addr string) *P2Pool { return &P2Pool{Address: addr} } func (p *P2Pool) GetPendingPayout() uint64 { if time.Since(p.LastFetchedPayout) > time.Minute*2 { jsonPayload := map[string]interface{}{} err := util.GetJson(fmt.Sprintf("%scurrent_payouts", ping.Selected.P2PoolURL), &jsonPayload) if err != nil { logging.Warnf("Unable to fetch p2pool payouts: %s", err.Error()) p.LastPayout = 0 } address := p.Address vtc, ok := jsonPayload[address].(float64) if !ok { p.LastFetchedPayout = time.Now() p.LastPayout = 0 } vtc *= 100000000 p.LastFetchedPayout = time.Now() p.LastPayout = uint64(vtc) } return p.LastPayout } func (p *P2Pool) GetStratumUrl() string { return ping.Selected.P2PoolStratum } func (p *P2Pool) GetUsername() string { return p.Address } func (p *P2Pool) GetPassword() string { return "x" } func (p *P2Pool) GetID() int { return 2 } func (p *P2Pool) GetName() string { return "P2Pool" } func (p *P2Pool) GetFee() (fee float64) { if time.Since(p.LastFetchedFee) > time.Minute*30 { jsonPayload := map[string]interface{}{} err := util.GetJson(fmt.Sprintf("%slocal_stats", ping.Selected.P2PoolURL), &jsonPayload) if err != nil { logging.Warnf("Unable to fetch p2pool fee: %s", err.Error()) return 2.0 } fee, ok := jsonPayload["fee"].(float64) if !ok { return 2.0 } donationFee, ok := jsonPayload["donation_proportion"].(float64) if !ok { return 2.0 } fee += donationFee p.LastFetchedFee = time.Now() p.LastFee = float64(fee) } return p.LastFee } func (p *P2Pool) OpenBrowserPayoutInfo(addr string) { util.OpenBrowser(ping.Selected.P2PoolURL) } ================================================ FILE: pools/p2proxy.go ================================================ package pools import ( "fmt" "github.com/vertcoin-project/one-click-miner-vnext/networks" "github.com/vertcoin-project/one-click-miner-vnext/util" ) var _ Pool = &P2Proxy{} type P2Proxy struct { Address string } func NewP2Proxy(addr string) *P2Proxy { return &P2Proxy{Address: addr} } func (p *P2Proxy) GetPendingPayout() uint64 { jsonPayload := map[string]interface{}{} err := util.GetJson(fmt.Sprintf("%sapi/balance?address=%s", networks.Active.P2ProxyURL, p.Address), &jsonPayload) if err != nil { return 0 } vtc, ok := jsonPayload[p.Address].(float64) if !ok { return 0 } vtc *= 100000000 return uint64(vtc) } func (p *P2Proxy) GetStratumUrl() string { return networks.Active.P2ProxyStratum } func (p *P2Proxy) GetUsername() string { return p.Address } func (p *P2Proxy) GetPassword() string { return "x" } func (p *P2Proxy) GetID() int { return 1 } func (p *P2Proxy) GetName() string { return "P2Proxy" } func (p *P2Proxy) GetFee() float64 { return 1.00 } func (p *P2Proxy) OpenBrowserPayoutInfo(addr string) {} ================================================ FILE: pools/pool.go ================================================ package pools type Pool interface { GetPendingPayout() uint64 GetStratumUrl() string GetUsername() string GetPassword() string GetName() string GetID() int GetFee() float64 OpenBrowserPayoutInfo(addr string) } func GetPools(addr string, testnet bool) []Pool { if testnet { return []Pool{ NewP2Proxy(addr), } } return []Pool{ NewZergpool(addr), NewSuprnova(addr), NewP2Pool(addr), Newzpool(addr), NewMiningpoolSweden(addr), } } func GetPool(pool int, addr string, testnet bool) Pool { pools := GetPools(addr, testnet) for _, p := range pools { if p.GetID() == pool { return p } } return pools[0] } ================================================ FILE: pools/suprnova.go ================================================ package pools import ( "fmt" "time" "github.com/vertcoin-project/one-click-miner-vnext/util" ) var _ Pool = &Suprnova{} type Suprnova struct { Address string LastFetchedPayout time.Time LastPayout uint64 } func NewSuprnova(addr string) *Suprnova { return &Suprnova{Address: addr} } func (p *Suprnova) GetPendingPayout() uint64 { jsonPayload := map[string]interface{}{} err := util.GetJson(fmt.Sprintf("https://vtc.suprnova.cc/index.php?page=api&action=getuserbalance&api_key=%s", p.Address), &jsonPayload) if err != nil { return 0 } el, ok := jsonPayload["getuserbalance"].(map[string]interface{}) if !ok { return 0 } el, ok = el["data"].(map[string]interface{}) if !ok { return 0 } confirmed, ok := el["confirmed"].(float64) if !ok { return 0 } unconfirmed, ok := el["unconfirmed"].(float64) if !ok { return 0 } vtc := confirmed + unconfirmed vtc *= 100000000 return uint64(vtc) } func (p *Suprnova) GetStratumUrl() string { return "stratum+tcp://vtc.suprnova.cc:1776" } func (p *Suprnova) GetUsername() string { return p.Address } func (p *Suprnova) GetPassword() string { return "x" } func (p *Suprnova) GetID() int { return 4 } func (p *Suprnova) GetName() string { return "Suprnova.cc" } func (p *Suprnova) GetFee() float64 { return 1.00 } func (p *Suprnova) OpenBrowserPayoutInfo(addr string) { util.OpenBrowser(fmt.Sprintf("https://vtc.suprnova.cc/index.php?page=anondashboard&user=%s", addr)) } ================================================ FILE: pools/zergpool.go ================================================ package pools import ( "fmt" "time" "github.com/vertcoin-project/one-click-miner-vnext/util" ) var _ Pool = &Zergpool{} type Zergpool struct { Address string LastFetchedPayout time.Time LastPayout uint64 } func NewZergpool(addr string) *Zergpool { return &Zergpool{Address: addr} } func (p *Zergpool) GetPendingPayout() uint64 { jsonPayload := map[string]interface{}{} err := util.GetJson(fmt.Sprintf("https://api.zergpool.com:8443/api/wallet?address=%s", p.Address), &jsonPayload) if err != nil { return 0 } vtc, ok := jsonPayload["unpaid"].(float64) if !ok { return 0 } vtc *= 100000000 return uint64(vtc) } func (p *Zergpool) GetStratumUrl() string { return "stratum+tcp://verthash.mine.zergpool.com:4534" } func (p *Zergpool) GetUsername() string { return p.Address } func (p *Zergpool) GetPassword() string { return "c=VTC,mc=VTC" } func (p *Zergpool) GetID() int { return 5 } func (p *Zergpool) GetName() string { return "Zergpool.com" } func (p *Zergpool) GetFee() float64 { return 0.50 } func (p *Zergpool) OpenBrowserPayoutInfo(addr string) { util.OpenBrowser(fmt.Sprintf("https://zergpool.com/?address=%s", addr)) } ================================================ FILE: pools/zpool.go ================================================ package pools import ( "fmt" "time" "github.com/vertcoin-project/one-click-miner-vnext/util" ) var _ Pool = &zpool{} type zpool struct { Address string LastFetchedPayout time.Time LastPayout uint64 } func Newzpool(addr string) *zpool { return &zpool{Address: addr} } func (p *zpool) GetPendingPayout() uint64 { jsonPayload := map[string]interface{}{} err := util.GetJson(fmt.Sprintf("https://zpool.ca/api/wallet?address=%s", p.Address), &jsonPayload) if err != nil { return 0 } vtc, ok := jsonPayload["unpaid"].(float64) if !ok { return 0 } vtc *= 100000000 return uint64(vtc) } func (p *zpool) GetStratumUrl() string { return "stratum+tcp://verthash.mine.zpool.ca:6144" } func (p *zpool) GetUsername() string { return p.Address } func (p *zpool) GetPassword() string { return "c=VTC,zap=VTC" } func (p *zpool) GetID() int { return 6 } func (p *zpool) GetName() string { return "Zpool.ca" } func (p *zpool) GetFee() float64 { return 0.50 } func (p *zpool) OpenBrowserPayoutInfo(addr string) { util.OpenBrowser(fmt.Sprintf("https://zpool.ca/wallet/%s", addr)) } ================================================ FILE: prerequisites/amdgpudriver.go ================================================ package prerequisites import ( "fmt" "os/exec" "runtime" "strings" ) func checkAmdgpuDriverInstalled() error { if runtime.GOOS == "linux" { Info := exec.Command("lsmod") History, _ := Info.Output() lines := strings.Split(string(History), "\n") for _, l := range lines { if strings.Contains(l, "amdgpu") { return nil } } return fmt.Errorf("AMD GPU Driver is not installed. You need to install it in order to run the miner") } // If we don't know, assume OK return nil } ================================================ FILE: prerequisites/msvcrt2013.go ================================================ package prerequisites import ( "bytes" "encoding/hex" "fmt" "io" "net/http" "os" "os/exec" "path/filepath" "strings" "github.com/vertcoin-project/one-click-miner-vnext/logging" "github.com/vertcoin-project/one-click-miner-vnext/util" ) func vcrt2013Installed() bool { searchPath := strings.Split(os.Getenv("PATH"), ";") for _, p := range searchPath { search := filepath.Join(p, "MSVCR120.dll") if _, err := os.Stat(search); !os.IsNotExist(err) { return true } } logging.Infof("Visual C++ Redistributable is not found\n") return false } func installVCRT2013(install chan bool) error { if vcrt2013Installed() { return nil } install <- true err := downloadVCRT2013() if err != nil { return err } err = checkVCRT2013Hash() if err != nil { return err } installer := exec.Command(vcrt2013DownloadPath(), "/q", "/norestart") err = installer.Run() install <- false return err } func vcrt2013DownloadPath() string { downloadDir := filepath.Join(util.DataDirectory(), "prerequisites") downloadPath := filepath.Join(downloadDir, "vcredist_x64.exe") err := os.MkdirAll(downloadDir, 0755) if err != nil && !os.IsExist(err) { logging.Errorf("Could not create download dir for msvcrt: %v", err) } return downloadPath } func checkVCRT2013Hash() error { expectedHash, _ := hex.DecodeString("e554425243e3e8ca1cd5fe550db41e6fa58a007c74fad400274b128452f38fb8") realHash, err := util.ShaSum(vcrt2013DownloadPath()) if err != nil { return err } if !bytes.Equal(realHash, expectedHash) { return fmt.Errorf("Hash of downloaded VCRT runtime installer does not match") } return nil } func downloadVCRT2013() error { resp, err := http.Get("https://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x64.exe") if err != nil { return err } defer resp.Body.Close() // Create the file out, err := os.Create(vcrt2013DownloadPath()) if err != nil { return err } defer out.Close() // Write the body to file _, err = io.Copy(out, resp.Body) return err } ================================================ FILE: prerequisites/nvidiadriver.go ================================================ package prerequisites import ( "fmt" "os/exec" "runtime" "strings" ) func checkNvidiaDriverInstalled() error { if runtime.GOOS == "linux" { Info := exec.Command("lsmod") History, _ := Info.Output() lines := strings.Split(string(History), "\n") for _, l := range lines { if strings.Contains(l, "nvidia") { return nil } } return fmt.Errorf("NVidia Driver is not installed. You need to install it in order to run the miner") } // If we don't know, assume OK return nil } ================================================ FILE: prerequisites/prerequisites.go ================================================ package prerequisites import ( "github.com/vertcoin-project/one-click-miner-vnext/logging" ) func Install(name string, install chan bool) error { logging.Infof("Installing prerequisite [%s]\n", name) switch name { case "msvcrt2013": return installVCRT2013(install) case "amddriverlinux": return checkAmdgpuDriverInstalled() case "nvidiadriverlinux": return checkNvidiaDriverInstalled() default: logging.Warnf("Unknown prerequisite requested: %s", name) } return nil } ================================================ FILE: project.json ================================================ { "name": "Vertcoin One Click Miner", "description": "Enter your project description", "author": { "name": "Gert-Jaap Glasbergen", "email": "gertjaap@gertjaap.org" }, "version": "0.1.0", "binaryname": "vertcoin-ocm", "frontend": { "dir": "frontend", "install": "npm install", "build": "npm run build", "bridge": "src", "serve": "npm run serve" } } ================================================ FILE: tracking/matomo.go ================================================ package tracking import ( "crypto/rand" "encoding/hex" "fmt" "log" "net/http" "os" "path/filepath" "runtime" "strings" "sync" "time" "github.com/vertcoin-project/one-click-miner-vnext/logging" "github.com/vertcoin-project/one-click-miner-vnext/util" ) type TrackingRequest struct { Category string Action string Name string } var trackChan chan TrackingRequest var trackingEnabled bool var waitGroup sync.WaitGroup func Enable() { trackingEnabled = true saveState() } func IsEnabled() bool { return trackingEnabled } func Disable() { trackingEnabled = false saveState() } func loadState() { dat, err := os.ReadFile(filepath.Join(util.DataDirectory(), "tracking")) if err != nil { Enable() return } trackingEnabled = (string(dat) == "1") } func saveState() { value := "1" if !trackingEnabled { value = "0" } err := os.WriteFile(filepath.Join(util.DataDirectory(), "tracking"), []byte(value), 0644) if err != nil { logging.Errorf("Error writing tracking state: %v", err) } } func StartTracker() { matomoClient := &http.Client{Timeout: 2 * time.Second} trackChan = make(chan TrackingRequest, 100) waitGroup = sync.WaitGroup{} loadState() new := true waitGroup.Add(1) go func() { for t := range trackChan { if !trackingEnabled { continue } req, err := http.NewRequest("GET", "https://analytics.javerity.com/matomo.php", nil) if err != nil { log.Print(err) os.Exit(1) } q := req.URL.Query() q.Add("idsite", "3") q.Add("rec", "1") if new { new = false q.Add("new_visit", "1") } q.Add("action_name", fmt.Sprintf("%s/%s", t.Category, t.Action)) q.Add("e_c", t.Category) q.Add("e_a", t.Action) q.Add("e_n", t.Name) q.Add("ua", userAgent()) q.Add("_id", visitorId()) req.URL.RawQuery = q.Encode() r, err := matomoClient.Do(req) if err != nil { logging.Warnf("Error sending tracking data: %v", err) continue } if r.Body != nil { r.Body.Close() } } waitGroup.Done() }() } func Stop() { close(trackChan) waitGroup.Wait() } func Track(req TrackingRequest) { trackChan <- req } var vId string func visitorId() string { if vId == "" { dat, err := os.ReadFile(filepath.Join(util.DataDirectory(), "unique_id")) if err != nil { dat = make([]byte, 8) _, err := rand.Read(dat) if err != nil { logging.Errorf("Error reading random ID: %v", err) } err = os.WriteFile(filepath.Join(util.DataDirectory(), "unique_id"), dat, 0644) if err != nil { logging.Errorf("Error writing random ID: %v", err) } } vId = strings.ToUpper(hex.EncodeToString(dat)) } return vId } var ua string func userAgent() string { if ua == "" { ua = fmt.Sprintf("OCM/%s %s/%s", GetVersion(), runtime.GOOS, runtime.GOARCH) } return ua } func GetVersion() string { return version } ================================================ FILE: tracking/version.go ================================================ package tracking var version = "0.0-alpha1" // Fixed dev version - always updates available ================================================ FILE: util/autostart.go ================================================ package util import ( "os" "path/filepath" "github.com/ProtonMail/go-autostart" "github.com/vertcoin-project/one-click-miner-vnext/logging" ) var app *autostart.App var fullPath string var oldFullPathFile string func init() { fullPath, _ = filepath.Abs(os.Args[0]) oldFullPath := "" oldFullPathFile = filepath.Join(DataDirectory(), "auto_start") if FileExists(oldFullPathFile) { oldFullPathBytes, err := os.ReadFile(oldFullPath) if err != nil { oldFullPath = string(oldFullPathBytes) } } app = &autostart.App{ Name: "vertcoin-ocm", DisplayName: "Vertcoin One-Click miner", Exec: []string{fullPath}, } if oldFullPath != "" && oldFullPath != fullPath { oldApp := &autostart.App{ Name: "vertcoin-ocm", DisplayName: "Vertcoin One-Click miner", Exec: []string{oldFullPath}, } if oldApp.IsEnabled() { // We enabled autostart on a different location. Move it to the current // full executable path. Disable it on the old path and move to the new. logging.Debugf("Autostart was enabled on a different location.\nOld location: %s\nNew location: %s\nMoving it.", oldFullPath, fullPath) err := oldApp.Disable() if err == nil { err := app.Enable() if err == nil { err = os.WriteFile(oldFullPathFile, []byte(fullPath), 0644) if err != nil { logging.Errorf("Writing outstart file failed: %v", err) } } } } } } func GetAutoStart() bool { return app.IsEnabled() } func SetAutoStart(autoStart bool) string { if autoStart { err := app.Enable() if err != nil { return err.Error() } // Store the full path we created the autostart for, so we can // re-enable it on a new path when someone decides to download // an update to a different location. err = os.WriteFile(oldFullPathFile, []byte(fullPath), 0644) if err != nil { return err.Error() } } else { err := app.Disable() if err != nil { return err.Error() } err = os.Remove(oldFullPathFile) if err != nil { return err.Error() } } return "" } ================================================ FILE: util/bech32/bech32.go ================================================ package bech32 import ( "fmt" "strings" ) // charset is the sequence of ascii characters that make up the bech32 // alphabet. Each character represents a 5-bit squashed byte. // q = 0b00000, p = 0b00001, z = 0b00010, and so on. const charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" // inverseCharset is a mapping of 8-bit ascii characters to the charset // positions. Both uppercase and lowercase ascii are mapped to the 5-bit // position values. var inverseCharset = [256]int8{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1} // Bytes8to5 extends a byte slice into a longer, padded byte slice of 5-bit elements // where the high 3 bits are all 0. func Bytes8to5(input []byte) []byte { // no way to triger an error going from 8 to 5 output, _ := ByteSquasher(input, 8, 5) return output } // Bytes5to8 goes from squashed bytes to full height bytes func Bytes5to8(input []byte) ([]byte, error) { return ByteSquasher(input, 5, 8) } // ByteSquasher squashes full-width (8-bit) bytes into "squashed" 5-bit bytes, // and vice versa. It can operate on other widths but in this package only // goes 5 to 8 and back again. It can return an error if the squashed input // you give it isn't actually squashed, or if there is padding (trailing q characters) // when going from 5 to 8 func ByteSquasher(input []byte, inputWidth, outputWidth uint32) ([]byte, error) { var bitstash, accumulator uint32 var output []byte maxOutputValue := uint32((1 << outputWidth) - 1) for i, c := range input { if c>>inputWidth != 0 { return nil, fmt.Errorf("byte %d (%x) high bits set", i, c) } accumulator = (accumulator << inputWidth) | uint32(c) bitstash += inputWidth for bitstash >= outputWidth { bitstash -= outputWidth output = append(output, byte((accumulator>>bitstash)&maxOutputValue)) } } // pad if going from 8 to 5 if inputWidth == 8 && outputWidth == 5 { if bitstash != 0 { output = append(output, byte((accumulator << (outputWidth - bitstash) & maxOutputValue))) } } else if bitstash >= inputWidth || ((accumulator<<(outputWidth-bitstash))&maxOutputValue) != 0 { // no pad from 5 to 8 allowed return nil, fmt.Errorf( "invalid padding from %d to %d bits", inputWidth, outputWidth) } return output, nil } // SquashedBytesToString swaps 5-bit bytes with a string of the corresponding letters func SquashedBytesToString(input []byte) (string, error) { var s string for i, c := range input { if c&0xe0 != 0 { return "", fmt.Errorf("high bits set at position %d: %x", i, c) } s += string(charset[c]) } return s, nil } // StringToSquashedBytes uses the inverseCharset to switch from the characters // back to 5-bit squashed bytes. func StringToSquashedBytes(input string) ([]byte, error) { b := make([]byte, len(input)) for i, c := range input { if inverseCharset[c] == -1 { return nil, fmt.Errorf("contains invalid character %s", string(c)) } b[i] = byte(inverseCharset[c]) } return b, nil } // PolyMod takes a byte slice and returns the 32-bit BCH checksum. // Note that the input bytes to PolyMod need to be squashed to 5-bits tall // before being used in this function. And this function will not error, // but instead return an unusable checksum, if you give it full-height bytes. func PolyMod(values []byte) uint32 { // magic generator uint32s gen := []uint32{ 0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3, } // start with 1 chk := uint32(1) for _, v := range values { top := chk >> 25 chk = (chk&0x1ffffff)<<5 ^ uint32(v) for i, g := range gen { if (top>>uint8(i))&1 == 1 { chk ^= g } } } return chk } // HRPExpand turns the human redable part into 5bit-bytes for later processing func HRPExpand(input string) []byte { output := make([]byte, (len(input)*2)+1) // first half is the input string shifted down 5 bits. // not much is going on there in terms of data / entropy for i, c := range input { output[i] = uint8(c) >> 5 } // then there's a 0 byte separator // don't need to set 0 byte in the middle, as it starts out that way // second half is the input string, with the top 3 bits zeroed. // most of the data / entropy will live here. for i, c := range input { output[i+len(input)+1] = uint8(c) & 0x1f } return output } // create checksum makes a 6-shortbyte checksum from the HRP and data parts func CreateChecksum(hrp string, data []byte) []byte { values := append(HRPExpand(hrp), data...) // put 6 zero bytes on at the end values = append(values, make([]byte, 6)...) //get checksum for whole slice // flip the LSB of the checksum data after creating it checksum := PolyMod(values) ^ 1 for i := 0; i < 6; i++ { // note that this is NOT the same as converting 8 to 5 // this is it's own expansion to 6 bytes from 4, chopping // off the MSBs. values[(len(values)-6)+i] = byte(checksum>>(5*(5-uint32(i)))) & 0x1f } return values[len(values)-6:] } func VerifyChecksum(hrp string, data []byte) bool { values := append(HRPExpand(hrp), data...) checksum := PolyMod(values) // make sure it's 1 (from the LSB flip in CreateChecksum return checksum == 1 } // Encode takes regular bytes of data, and an hrp prefix, and returns the // bech32 encoded string. It doesn't do any segwit specific encoding. func Encode(hrp string, data []byte) string { fiveData := Bytes8to5(data) return EncodeSquashed(hrp, fiveData) } // EncodeSquashed takes the hrp prefix, as well as byte data that has already // been squashed to 5-bits high, and returns the bech32 encoded string. // It does not return an error; if you give it non-squashed data it will return // an empty string. func EncodeSquashed(hrp string, data []byte) string { combined := append(data, CreateChecksum(hrp, data)...) // Should be squashed, return empty string if it's not. dataString, err := SquashedBytesToString(combined) if err != nil { return "" } return hrp + "1" + dataString } // Decode takes a bech32 encoded string and returns the hrp and the full-height // data. Can error out for various reasons, mostly problems in the string given. // Doesn't do anything segwit specific. func Decode(adr string) (string, []byte, error) { hrp, squashedData, err := DecodeSquashed(adr) if err != nil { return hrp, nil, err } data, err := Bytes5to8(squashedData) if err != nil { return hrp, nil, err } return hrp, data, nil } // DecodeSquashed is the same as Decode, but will return squashed 5-bit high // data. func DecodeSquashed(adr string) (string, []byte, error) { // make an all lowercase and all uppercase version of the input string lowAdr := strings.ToLower(adr) highAdr := strings.ToUpper(adr) // if there's mixed case, that's not OK if adr != lowAdr && adr != highAdr { return "", nil, fmt.Errorf("mixed case address") } // default to lowercase adr = lowAdr // find the last "1" and split there splitLoc := strings.LastIndex(adr, "1") if splitLoc == -1 { return "", nil, fmt.Errorf("1 separator not present in address") } // hrp comes before the split hrp := adr[0:splitLoc] // get squashed data data, err := StringToSquashedBytes(adr[splitLoc+1:]) if err != nil { return hrp, nil, err } // make sure checksum works sumOK := VerifyChecksum(hrp, data) if !sumOK { return hrp, nil, fmt.Errorf("Checksum invalid") } // chop off checksum to return only payload data = data[:len(data)-6] return hrp, data, nil } // Segwit addresses can't be used in Encode and Decode directly, because the // witness version is special and doesn't get squashed. GetHRP gets the // HRP without checking any validity. func GetHRP(adr string) (string, error) { splitLoc := strings.LastIndex(adr, "1") if splitLoc == -1 { return "", fmt.Errorf("1 separator not present in address") } return adr[0:splitLoc], nil } // SegWitAddressEncode takes an hrp and data and gives back a segwit address. // The data that goes in should be the full pkscript from the txout, including the // version byte and the pushdata byte. func SegWitAddressEncode(hrp string, data []byte) (string, error) { if len(data) < 4 { return "", fmt.Errorf("data too short (%d bytes)", len(data)) } // first byte is the version number. that shouldn't be more than // 16, so only 4 bits, doesn't need to be squashed version := data[0] // the next byte is the length. make sure it's right length := data[1] // the rest of the data is real data and needs to be squashed data = data[2:] if int(length) != len(data) { return "", fmt.Errorf( "push byte / payload length mismatch: %d, %d", length, len(data)) } // allow alts // if hrp != "bc" && hrp != "tb" { // return "", fmt.Errorf("prefix %s is not bitcoin or testnet", hrp) // } // 1 byte programs are not ok. Also 40 bytes should be enough for anyone. if len(data) < 2 || len(data) > 40 { return "", fmt.Errorf("Data length %d out of bounds", len(data)) } // Better get all your features in soon; only 16 possible script versions. if version > 16 { return "", fmt.Errorf("Invalid witness program version %d", data[0]) } // version 0 scripts can only be 20 bytes (p2wpkh) or 32 bytes (p2wsh) if version == 0 && len(data) != 20 && len(data) != 32 { return "", fmt.Errorf("expect 20 or 32 byte v0 witprog, got %d", len(data)) } // squash payload data squashedData := Bytes8to5(data) // prepend version byte squashedData = append([]byte{version}, squashedData...) address := EncodeSquashed(hrp, squashedData) return address, nil } // SegWitAddressDecode takes a segwit address and returns the pkscript that // can go directly into the txout. (includes version byte and data push byte) func SegWitAddressDecode(adr string) ([]byte, error) { _, squashedData, err := DecodeSquashed(adr) if err != nil { return nil, err } // the segwit version byte is directly put into a 5bit squashed byte // since it maxes out at 16, wasting ~1 byte instead of 4. version := squashedData[0] data, err := Bytes5to8(squashedData[1:]) if err != nil { return nil, err } // Allow alts // if hrp != "bc" && hrp != "tb" { // return nil, fmt.Errorf("prefix %s is not bitcoin or testnet", hrp) // } if len(data) < 2 || len(data) > 40 { return nil, fmt.Errorf("Data length %d out of bounds", len(data)) } if version > 16 { return nil, fmt.Errorf("Invalid witness program version %d", data[0]) } if version == 0 && len(data) != 20 && len(data) != 32 { return nil, fmt.Errorf("expect 20 or 32 byte v0 witprog, got %d", len(data)) } // first give version byte, then push length if version > 0 { version |= 0x80 } outputScript := append([]byte{version}, byte(len(data))) outputScript = append(outputScript, data...) return outputScript, nil } // SegWitV0Encode takes an hrp prefix string and a 20 or 32 byte witness program // hash, and turns it into a version 0 address. (it puts the 0 and pushdata in // for you. func SegWitV0Encode(hrp string, data []byte) (string, error) { if len(data) != 20 && len(data) != 32 { return "", fmt.Errorf("Invalid data length %d, expect 20 or 32", len(data)) } script := []byte{0, byte(len(data))} script = append(script, data...) return SegWitAddressEncode(hrp, script) } ================================================ FILE: util/gpus.go ================================================ package util import ( "os" "os/exec" "regexp" "runtime" "strings" "github.com/vertcoin-project/one-click-miner-vnext/logging" ) type GPUType int const ( GPUTypeOther GPUType = 0 GPUTypeAMD GPUType = 1 GPUTypeNVidia GPUType = 2 GPUTypeIntel GPUType = 3 ) type GPU struct { OSName string Type GPUType } type KnownGPU struct { RegExPattern string Type GPUType RegExp *regexp.Regexp } var gpusCache []GPU var gpusCached = false var knownGPUs = []KnownGPU{ /*KnownGPU{"Radeon( \\(TM\\))?( RX)? (Vega|[4-5][6-9]0)", GPUTypeAMD, nil}, KnownGPU{"AMD Radeon\\(TM\\) R[79] Graphics", GPUTypeAMD, nil}, KnownGPU{"AMD Radeon VII", GPUTypeAMD, nil}, KnownGPU{"NVIDIA P[0-9]{3}-[0-9]{3}", GPUTypeNVidia, nil}, KnownGPU{"NVIDIA GeForce (RTX )?(GTX )?(10|16|20|[7-9])[0-9]{2}( ti)?(MX)?", GPUTypeNVidia, nil}, KnownGPU{"Advanced Micro Devices, Inc. \\[AMD/ATI\\] .*", GPUTypeAMD, nil},*/ KnownGPU{".*NVIDIA.*", GPUTypeNVidia, nil}, KnownGPU{".*AMD.*", GPUTypeAMD, nil}, KnownGPU{".*Intel.*", GPUTypeIntel, nil}, KnownGPU{".*Radeon.*", GPUTypeAMD, nil}, } func init() { if os.Getenv("OCM_VIRTUALBOX") == "1" { knownGPUs = append(knownGPUs, KnownGPU{".*VirtualBox.*", GPUTypeIntel, nil}) } for i := range knownGPUs { knownGPUs[i].RegExp, _ = regexp.Compile(knownGPUs[i].RegExPattern) } } func GetGPUsFromStrings(names []string) []GPU { gpus := []GPU{} for _, n := range names { found := false for _, k := range knownGPUs { if k.RegExp.Match([]byte(n)) { logging.Debugf("GPU [%s] matched regex [%s]\n", n, k.RegExp) gpus = append(gpus, GPU{n, k.Type}) found = true break } } if !found { logging.Debugf("Unmatched GPU: [%s]\n", n) gpus = append(gpus, GPU{n, GPUTypeOther}) } } return gpus } func GetGPUs() []GPU { if !gpusCached { gpus := []string{} if runtime.GOOS == "windows" { info := exec.Command("cmd", "/C", "wmic path win32_VideoController get name") PrepareBackgroundCommand(info) history, _ := info.Output() possibleGpus := strings.Split(string(history), "\n") for _, g := range possibleGpus { g = strings.Trim(g, "\r ") if g != "" && g != "Name" { gpus = append(gpus, g) } } } else if runtime.GOOS == "linux" { Info := exec.Command("lspci") History, _ := Info.Output() lines := strings.Split(string(History), "\n") for _, l := range lines { vgaIdx := strings.Index(l, "VGA compatible: ") if vgaIdx > -1 { gpus = append(gpus, l[vgaIdx+16:]) } vgaIdx = strings.Index(l, "VGA compatible controller: ") if vgaIdx > -1 { gpus = append(gpus, l[vgaIdx+27:]) } vgaIdx = strings.Index(l, "3D controller: ") if vgaIdx > -1 { gpus = append(gpus, l[vgaIdx+15:]) } } } else if runtime.GOOS == "darwin" { Info := exec.Command("system_profiler", "SPDisplaysDataType") History, _ := Info.Output() lines := strings.Split(string(History), "\n") for _, l := range lines { csIdx := strings.Index(l, "Chipset Model: ") if csIdx > -1 { gpus = append(gpus, l[csIdx+15:]) } } } gpusCache = GetGPUsFromStrings(gpus) gpusCached = true } return gpusCache } ================================================ FILE: util/gpus_test.go ================================================ package util import ( "log" "testing" ) func TestNvidia(t *testing.T) { gpus := []string{"NVIDIA something", "NVIDIA GeForce 930MX", "NVIDIA GeForce GTX 1660 Ti", "NVIDIA GeForce RTX 2070", "NVIDIA P106-100", "NVIDIA Corporation GM107 [GeForce GTX 750 Ti] (rev a2)"} g := GetGPUsFromStrings(gpus) for i, gpu := range g { if gpu.Type != GPUTypeNVidia { log.Printf("Did not detect %s as NVIDIA!", gpus[i]) t.Fail() } } } func TestAMD(t *testing.T) { gpus := []string{"AMD something", "Radeon something", "Radeon (TM) RX 480 Graphics", "AMD Radeon(TM) R7 Graphics", "Radeon RX 480", "Radeon (TM) RX 560 Graphics"} g := GetGPUsFromStrings(gpus) for _, gpu := range g { if gpu.Type != GPUTypeAMD { t.Fail() } } } func TestInvalid(t *testing.T) { gpus := []string{"Intel Integrated Graphics"} g := GetGPUsFromStrings(gpus) for _, gpu := range g { if gpu.Type != GPUTypeOther { t.Fail() } } } ================================================ FILE: util/miners_linux.go ================================================ // +build !windows package util import ( "os/exec" ) func PrepareBackgroundCommand(cmd *exec.Cmd) { } ================================================ FILE: util/miners_windows.go ================================================ // +build windows package util import ( "os/exec" "syscall" ) func PrepareBackgroundCommand(cmd *exec.Cmd) { cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} } ================================================ FILE: util/util.go ================================================ package util import ( "archive/tar" "archive/zip" "bytes" "compress/gzip" "encoding/json" "fmt" "io" "math/big" "net/http" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "time" "github.com/btcsuite/fastsha256" "github.com/vertcoin-project/one-click-miner-vnext/logging" "github.com/vertcoin-project/one-click-miner-vnext/networks" ) const APP_NAME string = "vertcoin-ocm" func DataDirectory() string { if runtime.GOOS == "windows" { return filepath.Join(os.Getenv("APPDATA"), APP_NAME) } else if runtime.GOOS == "darwin" { return filepath.Join(os.Getenv("HOME"), "Library", "Application Support", APP_NAME) } else if runtime.GOOS == "linux" { return filepath.Join(os.Getenv("HOME"), fmt.Sprintf(".%s", strings.ToLower(APP_NAME))) } return "." } func ReplaceInFile(file string, find string, replace string) error { input, err := os.ReadFile(file) if err != nil { return err } output := bytes.Replace(input, []byte(find), []byte(replace), -1) if err = os.WriteFile(file, output, 0666); err != nil { return err } return nil } type BlocksResponse struct { Blocks []Block `json:"blocks"` Pagination InsightPagination `json:"pagination"` } type InsightPagination struct { Next string `json:"next"` Prev string `json:"prev"` } type Block struct { Hash string `json:"hash"` Height int64 `json:"height"` } type BlockResponse struct { Bits uint32 `json:"bits"` } type getInfoResponse struct { Difficulty float64 `json:"difficulty"` TipHeight int64 `json:"tipHeight"` BackendTipHeight int64 `json:"backendTipHeight"` } type VerthashMinerDeviceConfig struct { DeviceIndex int PCIeBus string OpenCL bool Name string Platform string } // Returns true if backend is good func CheckBackendStatus(backend string) bool { info := getInfoResponse{} url := fmt.Sprintf("%sinfo", backend) err := GetJson(url, &info) if err != nil { logging.Errorf("Backend server failed to respond: %v", err) return false } // If any of the following are 0, backend server has a problem if info.BackendTipHeight == 0 { return false } if info.TipHeight == 0 { return false } if info.Difficulty == 0 { return false } return true } func GetDifficulty() float64 { info := getInfoResponse{} url := fmt.Sprintf("%sinfo", networks.Active.OCMBackend) err := GetJson(url, &info) if err != nil { logging.Errorf("Error fetching difficulty: %v", err) } return info.Difficulty } func GetTipHeight() int64 { info := getInfoResponse{} url := fmt.Sprintf("%sinfo", networks.Active.OCMBackend) err := GetJson(url, &info) if err != nil { logging.Errorf("Error fetching tip height: %v", err) } return info.TipHeight } func GetNetHash() uint64 { difficulty := big.NewFloat(GetDifficulty()) factor := big.NewInt(0).Exp(big.NewInt(2), big.NewInt(48), nil) netHash := difficulty.Mul(difficulty, big.NewFloat(0).SetInt(factor)) u, _ := netHash.Quo(netHash, big.NewFloat(9830250)).Uint64() // 0xffff * blocktime in seconds logging.Debugf("Nethash: %d", u) return u } func GetCoinsPerDay(th int64) float64 { halvings := th / 840000 // Vertcoin undergoes a halving event every 840000 blocks if halvings >= 32 { // Vertcoin will undergo 32 halvings before zero emission return 0 } if halvings == 0 { return 28800 } cpd := float64(28800) for i := int64(0); i < halvings; i++ { cpd = cpd / 2 } return cpd } var jsonClient = &http.Client{Timeout: 60 * time.Second} func FileExists(filename string) bool { info, err := os.Stat(filename) if os.IsNotExist(err) { return false } return !info.IsDir() } func GetJson(url string, target interface{}) error { r, err := jsonClient.Get(url) if err != nil { return err } defer r.Body.Close() return json.NewDecoder(r.Body).Decode(target) } func PostJson(url string, payload interface{}, target interface{}) error { var b bytes.Buffer err := json.NewEncoder(&b).Encode(payload) if err != nil { return err } r, err := jsonClient.Post(url, "application/json", bytes.NewBuffer(b.Bytes())) if err != nil { return err } defer r.Body.Close() bodyBytes, err := io.ReadAll(r.Body) if err != nil { return err } logging.Infof("POST JSON response: %s", string(bodyBytes)) buf := bytes.NewBuffer(bodyBytes) return json.NewDecoder(buf).Decode(target) } func OpenBrowser(url string) { var err error switch runtime.GOOS { case "linux": err = exec.Command("xdg-open", url).Start() case "windows": err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() case "darwin": err = exec.Command("open", url).Start() default: err = fmt.Errorf("unsupported platform") } if err != nil { logging.Error(err) } } func UnpackZip(archive, unpackPath string) error { r, err := zip.OpenReader(archive) if err != nil { return err } defer r.Close() for _, f := range r.File { targetPath := filepath.Join(unpackPath, f.Name) // Check for ZipSlip. More Info: http://bit.ly/2MsjAWE if !strings.HasPrefix(targetPath, filepath.Clean(unpackPath)+string(os.PathSeparator)) { return fmt.Errorf("%s: illegal file path", targetPath) } if f.FileInfo().IsDir() { // Make Folder err = os.MkdirAll(targetPath, os.ModePerm) if err != nil && !os.IsExist(err) { return err } continue } err := os.MkdirAll(filepath.Dir(targetPath), 0755) if err != nil && !os.IsExist(err) { return err } outFile, err := os.OpenFile(targetPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) if err != nil { return err } defer outFile.Close() rc, err := f.Open() if err != nil { return err } defer rc.Close() _, err = io.Copy(outFile, rc) if err != nil { return err } } return nil } func UnpackTar(archive, unpackPath string) error { f, err := os.Open(archive) if err != nil { return err } defer f.Close() gzf, err := gzip.NewReader(f) if err != nil { return err } tarReader := tar.NewReader(gzf) for { header, err := tarReader.Next() if err == io.EOF { break } if err != nil { fmt.Println(err) os.Exit(1) } name := header.Name switch header.Typeflag { case tar.TypeDir: continue case tar.TypeReg: targetPath := filepath.Join(unpackPath, name) err = os.MkdirAll(filepath.Dir(targetPath), 0755) if err != nil && !os.IsExist(err) { return err } outFile, err := os.OpenFile(targetPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) if err != nil { return err } defer outFile.Close() _, err = io.Copy(outFile, tarReader) if err != nil { return err } } } return nil } func ShaSum(file string) ([]byte, error) { h := fastsha256.New() fp, err := os.Open(file) if err != nil { return []byte{}, err } defer fp.Close() buf := make([]byte, 4096) for { n, err := fp.Read(buf) if err != nil && err != io.EOF { return []byte{}, err } if err == io.EOF { break } else { h.Write(buf[:n]) } } return h.Sum(nil), nil } func ParseVerthashMinerDeviceCfg(cfg string) map[int]VerthashMinerDeviceConfig { lines := strings.Split(cfg, "\n") deviceCFG := VerthashMinerDeviceConfig{} allDeviceCFG := make(map[int]VerthashMinerDeviceConfig) platforms := make(map[int]string) // line 0 will always have the platform type if strings.Contains(lines[0], "OpenCL") { deviceCFG.OpenCL = true } else { deviceCFG.OpenCL = false } lines = lines[1:] //dont need to process the first line anymore... isGettingDeviceInfo := false isGettingPlatformInfo := false for _, line := range lines { if strings.Contains(line, "Available platforms") { isGettingPlatformInfo = true } if strings.Contains(line, "Available devices") { isGettingPlatformInfo = false } if isGettingPlatformInfo && strings.Contains(line, "Platform name") { parsedNum, _ := strconv.Atoi(string(line[2])) platforms[parsedNum-1] = strings.TrimSpace(strings.Split(line, ": ")[1]) } if strings.Contains(line, "DeviceIndex:") { // if we were already getting info, save that device and start again if isGettingDeviceInfo { allDeviceCFG[deviceCFG.DeviceIndex] = deviceCFG deviceCFG = VerthashMinerDeviceConfig{} } isGettingDeviceInfo = true tmpStr := strings.SplitAfter(line, " ") // in order to capture negative / 2 digit numbers deviceCFG.DeviceIndex, _ = strconv.Atoi(strings.TrimSpace(tmpStr[len(tmpStr)-1])) } if isGettingDeviceInfo && strings.Contains(line, "Name:") { deviceCFG.Name = strings.TrimSpace(strings.SplitAfter(line, ":")[1]) } if isGettingDeviceInfo && strings.Contains(line, "PCIeBusId:") { tmpStr := strings.SplitAfter(line, " ") deviceCFG.PCIeBus = strings.TrimSpace(tmpStr[len(tmpStr)-1]) } if isGettingDeviceInfo && strings.Contains(line, "Platform index:") { tmpStr := strings.SplitAfter(line, ": ") platformIdx, _ := strconv.Atoi(strings.TrimSpace(tmpStr[len(tmpStr)-1])) deviceCFG.Platform = platforms[platformIdx] } if isGettingDeviceInfo && strings.Contains(line, "#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#") { isGettingDeviceInfo = false } } allDeviceCFG[deviceCFG.DeviceIndex] = deviceCFG deviceCFG = VerthashMinerDeviceConfig{} return allDeviceCFG } ================================================ FILE: util/versioncheck.go ================================================ package util import ( "fmt" "strconv" "strings" "github.com/vertcoin-project/one-click-miner-vnext/logging" ) type GithubRelease struct { URL string `json:"html_url"` Tag string `json:"tag_name"` Body string `json:"body"` Draft bool `json:"draft"` Prerelease bool `json:"prerelease"` } var releases []GithubRelease func init() { err := GetJson("https://api.github.com/repos/vertcoin-project/one-click-miner-vnext/releases", &releases) if err != nil { logging.Errorf("Error fetching releases: %v", err) } } func GetLatestRelease() (GithubRelease, error) { for _, r := range releases { if !r.Draft { return r, nil } } return GithubRelease{}, fmt.Errorf("No release found") } func VersionStringToNumeric(ver string) int64 { verNum := int64(0) // split off suffix suffix := "" suffixIdx := strings.Index(ver, "-") if suffixIdx > -1 { suffix = ver[suffixIdx+1:] ver = ver[:suffixIdx] // Chop off possible git commit hash and "-dirty" suffixIdx = strings.Index(suffix, "-") if suffixIdx > -1 { suffix = suffix[:suffixIdx] } } if len(suffix) > 0 { if strings.Contains(suffix, "alpha") { verNum += -999 suffix = strings.ReplaceAll(suffix, "alpha", "") } if strings.Contains(suffix, "beta") { verNum += -899 suffix = strings.ReplaceAll(suffix, "beta", "") } suffixVal, _ := strconv.Atoi(suffix) verNum += int64(suffixVal) } versionParts := strings.Split(ver, ".") multiplier := int64(100000000) for _, v := range versionParts { verVal, _ := strconv.Atoi(v) verNum += (int64(verVal) * multiplier) multiplier /= 100 } return verNum } ================================================ FILE: util/versioncheck_test.go ================================================ package util import ( "fmt" "testing" ) func TestVersionStrings(t *testing.T) { testStrings := []string{"0.1-alpha1", "0.1.1", "0.2.1", "1.0-alpha1", "1.0", "0.1-alpha22-abe3f3b-dirty"} for _, s := range testStrings { fmt.Printf("%s = %d\n", s, VersionStringToNumeric(s)) } } ================================================ FILE: wallet/signsend.go ================================================ package wallet import ( "bytes" "encoding/hex" "fmt" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/vertcoin-project/one-click-miner-vnext/keyfile" "github.com/vertcoin-project/one-click-miner-vnext/networks" "github.com/vertcoin-project/one-click-miner-vnext/util" ) // SignMyInputs finds the inputs in a transaction that came from our own wallet, and signs them with our private keys. // Will modify the transaction in place, but will ignore inputs that we can't sign and leave them unsigned. func (w *Wallet) SignMyInputs(tx *wire.MsgTx, password string) error { // For now using only P2PKH signing - since we generate // a legacy address. Will have to use segwit stuff at some point // generate tx-wide hashCache for segwit stuff // might not be needed (non-witness) but make it anyway // hCache := txscript.NewTxSigHashes(tx) // make the stashes for signatures / witnesses sigStash := make([][]byte, len(tx.TxIn)) // get key privBytes, err := keyfile.LoadPrivateKey(password) if err != nil { return err } priv, _ := btcec.PrivKeyFromBytes(privBytes) for i := range tx.TxIn { sigStash[i], err = txscript.SignatureScript(tx, i, w.Script, txscript.SigHashAll, priv, true) if err != nil { return err } } // swap sigs into sigScripts in txins for i, txin := range tx.TxIn { if sigStash[i] != nil { txin.SignatureScript = sigStash[i] } } return nil } type txSend struct { RawTx string `json:"rawtx"` } type txSendReply struct { TxId string `json:"txid"` } func (w *Wallet) Send(tx *wire.MsgTx) (string, error) { var b bytes.Buffer err := tx.Serialize(&b) if err != nil { return "", err } s := txSend{ RawTx: hex.EncodeToString(b.Bytes()), } r := txSendReply{} err = util.PostJson(fmt.Sprintf("%stx", networks.Active.OCMBackend), s, &r) if err != nil { return "", err } return r.TxId, err } ================================================ FILE: wallet/sigops.go ================================================ package wallet import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/txscript" ) var WitnessScaleFactor = 4 // CountSigOps returns the number of signature operations for all transaction // input and output scripts in the provided transaction. This uses the // quicker, but imprecise, signature operation counting mechanism from // txscript. func CountSigOps(tx *btcutil.Tx) int { msgTx := tx.MsgTx() // Accumulate the number of signature operations in all transaction // inputs. totalSigOps := 0 for _, txIn := range msgTx.TxIn { numSigOps := txscript.GetSigOpCount(txIn.SignatureScript) totalSigOps += numSigOps } // Accumulate the number of signature operations in all transaction // outputs. for _, txOut := range msgTx.TxOut { numSigOps := txscript.GetSigOpCount(txOut.PkScript) totalSigOps += numSigOps } return totalSigOps } func (w *Wallet) GetSigOpCost(tx *btcutil.Tx, pkScript []byte, isCoinBaseTx bool, bip16, segWit bool) (int, error) { numSigOps := CountSigOps(tx) * WitnessScaleFactor if bip16 { numP2SHSigOps, err := w.CountP2SHSigOps(tx, isCoinBaseTx) if err != nil { return 0, nil } numSigOps += (numP2SHSigOps * WitnessScaleFactor) } if segWit && !isCoinBaseTx { msgTx := tx.MsgTx() for _, txIn := range msgTx.TxIn { witness := txIn.Witness sigScript := txIn.SignatureScript numSigOps += txscript.GetWitnessSigOpCount(sigScript, pkScript, witness) } } return numSigOps, nil } // CountP2SHSigOps returns the number of signature operations for all input // transactions which are of the pay-to-script-hash type. This uses the // precise, signature operation counting mechanism from the script engine which // requires access to the input transaction scripts. func (w *Wallet) CountP2SHSigOps(tx *btcutil.Tx, isCoinBaseTx bool) (int, error) { // We never spend P2SH in OCM, so this can be disabled return 0, nil /* // Coinbase transactions have no interesting inputs. if isCoinBaseTx { return 0, nil } // Accumulate the number of signature operations in all transaction // inputs. msgTx := tx.MsgTx() totalSigOps := 0 for txInIndex, txIn := range msgTx.TxIn { // Ensure the referenced input transaction is available. utxo := w.GetUtxo(txIn.PreviousOutPoint.Hash.String(), uint(txIn.PreviousOutPoint.Index)) if utxo.TxID == "" { str := fmt.Sprintf("output %v referenced from "+ "transaction %s:%d either does not exist or "+ "has already been spent", txIn.PreviousOutPoint, tx.Hash(), txInIndex) return 0, fmt.Errorf(str) } // We're only interested in pay-to-script-hash types, so skip // this input if it's not one. pkScript, _ := hex.DecodeString(utxo.ScriptPubKey) if !txscript.IsPayToScriptHash(pkScript) { continue } // Count the precise number of signature operations in the // referenced public key script. sigScript := txIn.SignatureScript numSigOps := txscript.GetPreciseSigOpCount(sigScript, pkScript, true) // We could potentially overflow the accumulator so check for // overflow. lastSigOps := totalSigOps totalSigOps += numSigOps if totalSigOps < lastSigOps { str := fmt.Sprintf("the public key script from output "+ "%v contains too many signature operations - "+ "overflow", txIn.PreviousOutPoint) return 0, fmt.Errorf(str) } } return totalSigOps, nil*/ } ================================================ FILE: wallet/wallet.go ================================================ package wallet import ( "errors" "fmt" "math" "path/filepath" "strings" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/base58" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/tidwall/buntdb" "github.com/vertcoin-project/one-click-miner-vnext/logging" "github.com/vertcoin-project/one-click-miner-vnext/networks" "github.com/vertcoin-project/one-click-miner-vnext/util" "github.com/vertcoin-project/one-click-miner-vnext/util/bech32" ) type Wallet struct { Address string Script []byte Spendable uint64 Maturing uint64 db *buntdb.DB } type Utxo struct { TxID string `json:"txid"` Vout uint `json:"vout"` Amount uint64 `json:"satoshis"` } // Insight's limit is 100kB HEX (so 50kB raw bytes) - limiting this to 45kB. Once we have // better backends (an insight that performs better and allows higher limits, an integrated // full node or just using Vertcoin Core) we can scale this up. var maxTxSize = 45000 func NewWallet(addr string, script []byte) (*Wallet, error) { logging.Infof("Initializing wallet %s", addr) db, err := buntdb.Open(filepath.Join(util.DataDirectory(), networks.Active.WalletDB)) if err != nil { return nil, err } return &Wallet{Address: addr, Script: script, db: db}, nil } func (w *Wallet) Utxos() ([]Utxo, error) { utxos := []Utxo{} err := util.GetJson(fmt.Sprintf("%sutxos/%x", networks.Active.OCMBackend, w.Script), &utxos) if err != nil { logging.Errorf("Error fetching UTXOs from OCM Backend: %s", err.Error()) return utxos, err } return utxos, nil } func (w *Wallet) PrepareSweep(addr string) ([]*wire.MsgTx, error) { utxos, err := w.Utxos() if err != nil { return nil, errors.New("backend_failure") } retArr := make([]*wire.MsgTx, 0) for { tx := wire.NewMsgTx(2) totalIn := uint64(0) for _, u := range utxos { alreadyIncluded := false for _, t := range retArr { for _, i := range t.TxIn { if i.PreviousOutPoint.Hash.String() == u.TxID && i.PreviousOutPoint.Index == uint32(u.Vout) { alreadyIncluded = true break } } } if alreadyIncluded { logging.Debugf("UTXO Already Included: %v", u) continue } totalIn += u.Amount h, _ := chainhash.NewHashFromStr(u.TxID) tx.AddTxIn(wire.NewTxIn(wire.NewOutPoint(h, uint32(u.Vout)), w.Script, nil)) } if len(tx.TxIn) == 0 { logging.Warnf("Trying to sweep with zero UTXOs") return nil, errors.New("insufficient_funds") } hash, version, err := base58.CheckDecode(addr) if err == nil && version == networks.Active.Base58P2PKHVersion { pubKeyHash := hash if err != nil { return nil, fmt.Errorf("invalid_address") } if len(pubKeyHash) != 20 { return nil, fmt.Errorf("invalid_address") } p2pkhScript, err := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP). AddOp(txscript.OP_HASH160).AddData(pubKeyHash). AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).Script() if err != nil { return nil, fmt.Errorf("script_failure") } tx.AddTxOut(wire.NewTxOut(0, p2pkhScript)) } else if err == nil && version == networks.Active.Base58P2SHVersion { scriptHash := hash if err != nil { return nil, fmt.Errorf("invalid_address") } if len(scriptHash) != 20 { return nil, fmt.Errorf("invalid_address") } p2shScript, err := txscript.NewScriptBuilder().AddOp(txscript.OP_HASH160).AddData(scriptHash).AddOp(txscript.OP_EQUAL).Script() if err != nil { return nil, fmt.Errorf("script_failure") } tx.AddTxOut(wire.NewTxOut(0, p2shScript)) } else if strings.HasPrefix(addr, fmt.Sprintf("%s1", networks.Active.Bech32Prefix)) { script, err := bech32.SegWitAddressDecode(addr) if err != nil { return nil, fmt.Errorf("invalid_address") } tx.AddTxOut(wire.NewTxOut(int64(totalIn), script)) } else { return nil, fmt.Errorf("invalid_address") } for i := range tx.TxIn { tx.TxIn[i].SignatureScript = make([]byte, 107) // add dummy signature to properly calculate size } // Weight = (stripped_size * 4) + witness_size formula, // using only serialization with and without witness data. As witness_size // is equal to total_size - stripped_size, this formula is identical to: // weight = (stripped_size * 3) + total_size. logging.Debugf("Transaction raw serialize size is %d\n", tx.SerializeSize()) logging.Debugf("Transaction serialize size stripped is %d\n", tx.SerializeSizeStripped()) chunked := false // Chunk if needed if tx.SerializeSize() > maxTxSize { chunked = true // Remove some extra inputs so we have enough for the next TX to remain valid, we // want to have enough money to create an output with enough value valueRemoved := uint64(0) for tx.SerializeSize() > maxTxSize || valueRemoved < 100000 { for _, u := range utxos { if u.TxID == tx.TxIn[len(tx.TxIn)-1].PreviousOutPoint.Hash.String() && uint32(u.Vout) == tx.TxIn[len(tx.TxIn)-1].PreviousOutPoint.Index { totalIn -= u.Amount valueRemoved += u.Amount } } tx.TxIn = tx.TxIn[:len(tx.TxIn)-1] } } txWeight := (tx.SerializeSizeStripped() * 3) + tx.SerializeSize() logging.Debugf("Transaction weight is %d\n", txWeight) btcTx := btcutil.NewTx(tx) sigOpCost, err := w.GetSigOpCost(btcTx, w.Script, false, true, true) if err != nil { return nil, fmt.Errorf("could_not_calculate_fee") } logging.Debugf("Transaction sigop cost is %d\n", sigOpCost) vSize := (math.Max(float64(txWeight), float64(sigOpCost*20)) + float64(3)) / float64(4) logging.Debugf("Transaction vSize is %.4f\n", vSize) vSizeInt := uint64(vSize + float64(0.5)) // Round Up logging.Debugf("Transaction vSizeInt is %d\n", vSizeInt) fee := uint64(vSizeInt * 100) logging.Debugf("Setting fee to %d\n", fee) // empty out the dummy sigs for i := range tx.TxIn { tx.TxIn[i].SignatureScript = nil } tx.TxOut[0].Value = int64(totalIn - fee) if tx.TxOut[0].Value < 50000 { return nil, fmt.Errorf("insufficient_funds") } retArr = append(retArr, tx) if !chunked { break } } return retArr, nil } func DirectWPKHScriptFromPKH(pkh [20]byte) []byte { builder := txscript.NewScriptBuilder() builder.AddOp(txscript.OP_0).AddData(pkh[:]) b, _ := builder.Script() return b } type BalanceResponse struct { Spendable uint64 `json:"confirmed"` Maturing uint64 `json:"maturing"` } // Update will reload balance from the backend func (w *Wallet) Update() { bal := BalanceResponse{} err := util.GetJson(fmt.Sprintf("%sbalance/%x", networks.Active.OCMBackend, w.Script), &bal) if err != nil { logging.Errorf("Error fetching balance from backend: %s", err.Error()) return } w.Spendable = bal.Spendable w.Maturing = bal.Maturing } // GetBalance will scan the utxos in the wallet and return // two values: mature and immature balance. Mining outputs // need to wait for 101 confirmations before being allowed // to spend func (w *Wallet) GetBalance() (bal uint64, balImmature uint64) { return w.Spendable, w.Maturing }