├── frontend ├── package.json.md5 ├── babel.config.js ├── src │ ├── assets │ │ ├── fonts │ │ │ ├── montserrat-v13-latin-regular.eot │ │ │ ├── montserrat-v13-latin-regular.ttf │ │ │ ├── montserrat-v13-latin-regular.woff │ │ │ └── montserrat-v13-latin-regular.woff2 │ │ └── css │ │ │ └── main.css │ ├── components │ │ ├── Update.vue │ │ ├── TabBar.vue │ │ ├── Tracking.vue │ │ ├── Welcome.vue │ │ └── Checks.vue │ ├── main.js │ ├── i18n │ │ ├── zh.json │ │ ├── ja.json │ │ ├── README.md │ │ ├── no.json │ │ ├── hi.json │ │ ├── sl.json │ │ ├── hr.json │ │ ├── ro.json │ │ ├── fr.json │ │ ├── sv.json │ │ ├── tr.json │ │ ├── pa.json │ │ ├── pl.json │ │ ├── ru.json │ │ ├── da.json │ │ └── lt.json │ └── App.vue ├── .gitignore ├── README.md ├── vue.config.js └── package.json ├── appicon.png ├── .gitignore ├── tracking ├── version.go └── matomo.go ├── util ├── miners_linux.go ├── miners_windows.go ├── versioncheck_test.go ├── gpus_test.go ├── versioncheck.go ├── autostart.go └── gpus.go ├── project.json ├── prerequisites ├── prerequisites.go ├── amdgpudriver.go ├── nvidiadriver.go └── msvcrt2013.go ├── backend ├── tracking.go ├── update.go ├── languages.go ├── backend.go └── wallet.go ├── payouts ├── trxpayout.go ├── xvgpayout.go ├── btcpayout.go ├── dgbpayout.go ├── kmdpayout.go ├── xmrpayout.go ├── zecpayout.go ├── zenpayout.go ├── dashpayout.go ├── ltcpayout.go ├── ppcpayout.go ├── rtmpayout.go ├── rvnpayout.go ├── vtcpayout.go ├── bchpayout.go ├── bnbpayout.go ├── firopayout.go ├── fluxpayout.go ├── grspayout.go ├── lbcpayout.go ├── cakepayout.go ├── etcpayout.go ├── maticpayout.go ├── shibpayout.go ├── dogepayout.go ├── usdtpayout.go └── payout.go ├── .github ├── ISSUE_TEMPLATE.md └── workflows │ └── golangci-lint.yml ├── pools ├── pool.go ├── hashcryptos.go ├── zpool.go ├── zergpool.go └── p2pool.go ├── LICENSE ├── networks └── networks.go ├── p2pool_nodes.json ├── wallet ├── signsend.go └── sigops.go ├── ADD-A-COIN.md ├── go.mod ├── wallet_doge ├── signsend.go └── sigops.go ├── miners ├── ccminer.go ├── teamredminer.go ├── cryptodredge.go └── lyclminer.go ├── main.go ├── logging └── log.go ├── README.md └── keyfile └── keyfile.go /frontend/package.json.md5: -------------------------------------------------------------------------------- 1 | 220535ccca5ef20ccfb6d9cc83ef5ddf -------------------------------------------------------------------------------- /appicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vertiond/verthash-one-click-miner/HEAD/appicon.png -------------------------------------------------------------------------------- /frontend/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | verthash-ocm 2 | *.zip 3 | .vscode/ 4 | verthash-ocm.exe 5 | *.code-workspace 6 | *.exe 7 | *.manifest 8 | *.syso -------------------------------------------------------------------------------- /tracking/version.go: -------------------------------------------------------------------------------- 1 | package tracking 2 | 3 | var version = "1.0-rc1" // Fixed dev version - always updates available 4 | -------------------------------------------------------------------------------- /util/miners_linux.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package util 4 | 5 | import ( 6 | "os/exec" 7 | ) 8 | 9 | func PrepareBackgroundCommand(cmd *exec.Cmd) { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/assets/fonts/montserrat-v13-latin-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vertiond/verthash-one-click-miner/HEAD/frontend/src/assets/fonts/montserrat-v13-latin-regular.eot -------------------------------------------------------------------------------- /frontend/src/assets/fonts/montserrat-v13-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vertiond/verthash-one-click-miner/HEAD/frontend/src/assets/fonts/montserrat-v13-latin-regular.ttf -------------------------------------------------------------------------------- /frontend/src/assets/fonts/montserrat-v13-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vertiond/verthash-one-click-miner/HEAD/frontend/src/assets/fonts/montserrat-v13-latin-regular.woff -------------------------------------------------------------------------------- /frontend/src/assets/fonts/montserrat-v13-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vertiond/verthash-one-click-miner/HEAD/frontend/src/assets/fonts/montserrat-v13-latin-regular.woff2 -------------------------------------------------------------------------------- /util/miners_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package util 4 | 5 | import ( 6 | "os/exec" 7 | "syscall" 8 | ) 9 | 10 | func PrepareBackgroundCommand(cmd *exec.Cmd) { 11 | cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} 12 | } 13 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /util/versioncheck_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestVersionStrings(t *testing.T) { 9 | testStrings := []string{"0.1-alpha1", "0.1.1", "0.2.1", "1.0-alpha1", "1.0", "0.1-alpha22-abe3f3b-dirty"} 10 | for _, s := range testStrings { 11 | fmt.Printf("%s = %d\n", s, VersionStringToNumeric(s)) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Verthash One Click Miner", 3 | "description": "Enter your project description", 4 | "author": { 5 | "name": "Gert-Jaap Glasbergen", 6 | "email": "gertjaap@gertjaap.org" 7 | }, 8 | "version": "0.1.0", 9 | "binaryname": "verthash-ocm", 10 | "frontend": { 11 | "dir": "frontend", 12 | "install": "npm install", 13 | "build": "npm run build", 14 | "bridge": "src", 15 | "serve": "npm run serve" 16 | } 17 | } -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # vue basic 2 | 3 | ## Project setup 4 | 5 | ``` 6 | npm install 7 | ``` 8 | 9 | ### Compiles and hot-reloads for development 10 | 11 | ``` 12 | npm run serve 13 | ``` 14 | 15 | ### Compiles and minifies for production 16 | 17 | ``` 18 | npm run build 19 | ``` 20 | 21 | ### Run your tests 22 | 23 | ``` 24 | npm run test 25 | ``` 26 | 27 | ### Lints and fixes files 28 | 29 | ``` 30 | npm run lint 31 | ``` 32 | 33 | ### Customize configuration 34 | 35 | See [Configuration Reference](https://cli.vuejs.org/config/). 36 | -------------------------------------------------------------------------------- /prerequisites/prerequisites.go: -------------------------------------------------------------------------------- 1 | package prerequisites 2 | 3 | import ( 4 | "github.com/vertiond/verthash-one-click-miner/logging" 5 | ) 6 | 7 | func Install(name string, install chan bool) error { 8 | logging.Infof("Installing prerequisite [%s]\n", name) 9 | switch name { 10 | case "msvcrt2013": 11 | return installVCRT2013(install) 12 | case "amddriverlinux": 13 | return checkAmdgpuDriverInstalled() 14 | case "nvidiadriverlinux": 15 | return checkNvidiaDriverInstalled() 16 | default: 17 | logging.Warnf("Unknown prerequisite requested: %s", name) 18 | } 19 | 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /backend/tracking.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "github.com/vertiond/verthash-one-click-miner/tracking" 5 | "github.com/vertiond/verthash-one-click-miner/util" 6 | ) 7 | 8 | func (m *Backend) EnableTracking() { 9 | tracking.Enable() 10 | } 11 | 12 | func (m *Backend) DisableTracking() { 13 | tracking.Disable() 14 | } 15 | 16 | func (m *Backend) TrackingEnabled() string { 17 | if tracking.IsEnabled() { 18 | return "1" 19 | } 20 | return "0" 21 | } 22 | 23 | func (m *Backend) ReportIssue() { 24 | util.OpenBrowser("https://github.com/vertiond/verthash-one-click-miner/issues/new") 25 | } 26 | -------------------------------------------------------------------------------- /prerequisites/amdgpudriver.go: -------------------------------------------------------------------------------- 1 | package prerequisites 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | "runtime" 7 | "strings" 8 | ) 9 | 10 | func checkAmdgpuDriverInstalled() error { 11 | if runtime.GOOS == "linux" { 12 | Info := exec.Command("lsmod") 13 | History, _ := Info.Output() 14 | lines := strings.Split(string(History), "\n") 15 | for _, l := range lines { 16 | if strings.Contains(l, "amdgpu") { 17 | return nil 18 | } 19 | } 20 | return fmt.Errorf("AMD GPU Driver is not installed. You need to install it in order to run the miner") 21 | } 22 | 23 | // If we don't know, assume OK 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /prerequisites/nvidiadriver.go: -------------------------------------------------------------------------------- 1 | package prerequisites 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | "runtime" 7 | "strings" 8 | ) 9 | 10 | func checkNvidiaDriverInstalled() error { 11 | if runtime.GOOS == "linux" { 12 | Info := exec.Command("lsmod") 13 | History, _ := Info.Output() 14 | lines := strings.Split(string(History), "\n") 15 | for _, l := range lines { 16 | if strings.Contains(l, "nvidia") { 17 | return nil 18 | } 19 | } 20 | return fmt.Errorf("NVidia Driver is not installed. You need to install it in order to run the miner") 21 | } 22 | 23 | // If we don't know, assume OK 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /payouts/trxpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &TRXPayout{} 4 | 5 | type TRXPayout struct{} 6 | 7 | func NewTRXPayout() *TRXPayout { 8 | return &TRXPayout{} 9 | } 10 | 11 | func (p *TRXPayout) GetID() int { 12 | return 26 13 | } 14 | 15 | func (p *TRXPayout) GetDisplayName() string { 16 | return "Tron" 17 | } 18 | 19 | func (p *TRXPayout) GetTicker() string { 20 | return "TRX" 21 | } 22 | 23 | func (p *TRXPayout) GetCoingeckoExchange() string { 24 | return "binance" 25 | } 26 | 27 | func (p *TRXPayout) GetCoingeckoCoinID() string { 28 | return "tron" 29 | } 30 | 31 | func (p *TRXPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/xvgpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &XVGPayout{} 4 | 5 | type XVGPayout struct{} 6 | 7 | func NewXVGPayout() *XVGPayout { 8 | return &XVGPayout{} 9 | } 10 | 11 | func (p *XVGPayout) GetID() int { 12 | return 18 13 | } 14 | 15 | func (p *XVGPayout) GetDisplayName() string { 16 | return "Verge" 17 | } 18 | 19 | func (p *XVGPayout) GetTicker() string { 20 | return "XVG" 21 | } 22 | 23 | func (p *XVGPayout) GetCoingeckoExchange() string { 24 | return "bittrex" 25 | } 26 | 27 | func (p *XVGPayout) GetCoingeckoCoinID() string { 28 | return "verge" 29 | } 30 | 31 | func (p *XVGPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/btcpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &BTCPayout{} 4 | 5 | type BTCPayout struct {} 6 | 7 | func NewBTCPayout() *BTCPayout { 8 | return &BTCPayout{} 9 | } 10 | 11 | func (p *BTCPayout) GetID() int { 12 | return 2 13 | } 14 | 15 | func (p *BTCPayout) GetDisplayName() string { 16 | return "Bitcoin" 17 | } 18 | 19 | func (p *BTCPayout) GetTicker() string { 20 | return "BTC" 21 | } 22 | 23 | func (p *BTCPayout) GetCoingeckoExchange() string { 24 | return "bittrex" 25 | } 26 | 27 | func (p *BTCPayout) GetCoingeckoCoinID() string { 28 | return "bitcoin" 29 | } 30 | 31 | func (p *BTCPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/dgbpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &DGBPayout{} 4 | 5 | type DGBPayout struct{} 6 | 7 | func NewDGBPayout() *DGBPayout { 8 | return &DGBPayout{} 9 | } 10 | 11 | func (p *DGBPayout) GetID() int { 12 | return 9 13 | } 14 | 15 | func (p *DGBPayout) GetDisplayName() string { 16 | return "Digibyte" 17 | } 18 | 19 | func (p *DGBPayout) GetTicker() string { 20 | return "DGB" 21 | } 22 | 23 | func (p *DGBPayout) GetCoingeckoExchange() string { 24 | return "bittrex" 25 | } 26 | 27 | func (p *DGBPayout) GetCoingeckoCoinID() string { 28 | return "digibyte" 29 | } 30 | 31 | func (p *DGBPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/kmdpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &KMDPayout{} 4 | 5 | type KMDPayout struct{} 6 | 7 | func NewKMDPayout() *KMDPayout { 8 | return &KMDPayout{} 9 | } 10 | 11 | func (p *KMDPayout) GetID() int { 12 | return 14 13 | } 14 | 15 | func (p *KMDPayout) GetDisplayName() string { 16 | return "Komodo" 17 | } 18 | 19 | func (p *KMDPayout) GetTicker() string { 20 | return "KMD" 21 | } 22 | 23 | func (p *KMDPayout) GetCoingeckoExchange() string { 24 | return "bittrex" 25 | } 26 | 27 | func (p *KMDPayout) GetCoingeckoCoinID() string { 28 | return "komodo" 29 | } 30 | 31 | func (p *KMDPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/xmrpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &XMRPayout{} 4 | 5 | type XMRPayout struct{} 6 | 7 | func NewXMRPayout() *XMRPayout { 8 | return &XMRPayout{} 9 | } 10 | 11 | func (p *XMRPayout) GetID() int { 12 | return 12 13 | } 14 | 15 | func (p *XMRPayout) GetDisplayName() string { 16 | return "Monero" 17 | } 18 | 19 | func (p *XMRPayout) GetTicker() string { 20 | return "XMR" 21 | } 22 | 23 | func (p *XMRPayout) GetCoingeckoExchange() string { 24 | return "binance" 25 | } 26 | 27 | func (p *XMRPayout) GetCoingeckoCoinID() string { 28 | return "monero" 29 | } 30 | 31 | func (p *XMRPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/zecpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &ZECPayout{} 4 | 5 | type ZECPayout struct{} 6 | 7 | func NewZECPayout() *ZECPayout { 8 | return &ZECPayout{} 9 | } 10 | 11 | func (p *ZECPayout) GetID() int { 12 | return 19 13 | } 14 | 15 | func (p *ZECPayout) GetDisplayName() string { 16 | return "Zcash" 17 | } 18 | 19 | func (p *ZECPayout) GetTicker() string { 20 | return "ZEC" 21 | } 22 | 23 | func (p *ZECPayout) GetCoingeckoExchange() string { 24 | return "bitfinex" 25 | } 26 | 27 | func (p *ZECPayout) GetCoingeckoCoinID() string { 28 | return "zcash" 29 | } 30 | 31 | func (p *ZECPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/zenpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &ZENPayout{} 4 | 5 | type ZENPayout struct{} 6 | 7 | func NewZENPayout() *ZENPayout { 8 | return &ZENPayout{} 9 | } 10 | 11 | func (p *ZENPayout) GetID() int { 12 | return 20 13 | } 14 | 15 | func (p *ZENPayout) GetDisplayName() string { 16 | return "Horizen" 17 | } 18 | 19 | func (p *ZENPayout) GetTicker() string { 20 | return "ZEN" 21 | } 22 | 23 | func (p *ZENPayout) GetCoingeckoExchange() string { 24 | return "binance" 25 | } 26 | 27 | func (p *ZENPayout) GetCoingeckoCoinID() string { 28 | return "zencash" 29 | } 30 | 31 | func (p *ZENPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/dashpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &DASHPayout{} 4 | 5 | type DASHPayout struct{} 6 | 7 | func NewDASHPayout() *DASHPayout { 8 | return &DASHPayout{} 9 | } 10 | 11 | func (p *DASHPayout) GetID() int { 12 | return 6 13 | } 14 | 15 | func (p *DASHPayout) GetDisplayName() string { 16 | return "Dash" 17 | } 18 | 19 | func (p *DASHPayout) GetTicker() string { 20 | return "DASH" 21 | } 22 | 23 | func (p *DASHPayout) GetCoingeckoExchange() string { 24 | return "binance" 25 | } 26 | 27 | func (p *DASHPayout) GetCoingeckoCoinID() string { 28 | return "dash" 29 | } 30 | 31 | func (p *DASHPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/ltcpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = <CPayout{} 4 | 5 | type LTCPayout struct {} 6 | 7 | func NewLTCPayout() *LTCPayout { 8 | return <CPayout{} 9 | } 10 | 11 | func (p *LTCPayout) GetID() int { 12 | return 3 13 | } 14 | 15 | func (p *LTCPayout) GetDisplayName() string { 16 | return "Litecoin" 17 | } 18 | 19 | func (p *LTCPayout) GetTicker() string { 20 | return "LTC" 21 | } 22 | 23 | func (p *LTCPayout) GetCoingeckoExchange() string { 24 | return "bittrex" 25 | } 26 | 27 | func (p *LTCPayout) GetCoingeckoCoinID() string { 28 | return "litecoin" 29 | } 30 | 31 | func (p *LTCPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/ppcpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &PPCPayout{} 4 | 5 | type PPCPayout struct{} 6 | 7 | func NewPPCPayout() *PPCPayout { 8 | return &PPCPayout{} 9 | } 10 | 11 | func (p *PPCPayout) GetID() int { 12 | return 16 13 | } 14 | 15 | func (p *PPCPayout) GetDisplayName() string { 16 | return "Peercoin" 17 | } 18 | 19 | func (p *PPCPayout) GetTicker() string { 20 | return "PPC" 21 | } 22 | 23 | func (p *PPCPayout) GetCoingeckoExchange() string { 24 | return "bittrex" 25 | } 26 | 27 | func (p *PPCPayout) GetCoingeckoCoinID() string { 28 | return "peercoin" 29 | } 30 | 31 | func (p *PPCPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/rtmpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &RTMPayout{} 4 | 5 | type RTMPayout struct{} 6 | 7 | func NewRTMPayout() *RTMPayout { 8 | return &RTMPayout{} 9 | } 10 | 11 | func (p *RTMPayout) GetID() int { 12 | return 22 13 | } 14 | 15 | func (p *RTMPayout) GetDisplayName() string { 16 | return "Raptoreum" 17 | } 18 | 19 | func (p *RTMPayout) GetTicker() string { 20 | return "RTM" 21 | } 22 | 23 | func (p *RTMPayout) GetCoingeckoExchange() string { 24 | return "coinex" 25 | } 26 | 27 | func (p *RTMPayout) GetCoingeckoCoinID() string { 28 | return "raptoreum" 29 | } 30 | 31 | func (p *RTMPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/rvnpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &RVNPayout{} 4 | 5 | type RVNPayout struct{} 6 | 7 | func NewRVNPayout() *RVNPayout { 8 | return &RVNPayout{} 9 | } 10 | 11 | func (p *RVNPayout) GetID() int { 12 | return 8 13 | } 14 | 15 | func (p *RVNPayout) GetDisplayName() string { 16 | return "Ravencoin" 17 | } 18 | 19 | func (p *RVNPayout) GetTicker() string { 20 | return "RVN" 21 | } 22 | 23 | func (p *RVNPayout) GetCoingeckoExchange() string { 24 | return "bittrex" 25 | } 26 | 27 | func (p *RVNPayout) GetCoingeckoCoinID() string { 28 | return "ravencoin" 29 | } 30 | 31 | func (p *RVNPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/vtcpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &VTCPayout{} 4 | 5 | type VTCPayout struct {} 6 | 7 | func NewVTCPayout() *VTCPayout { 8 | return &VTCPayout{} 9 | } 10 | 11 | func (p *VTCPayout) GetID() int { 12 | return 1 13 | } 14 | 15 | func (p *VTCPayout) GetDisplayName() string { 16 | return "Vertcoin" 17 | } 18 | 19 | func (p *VTCPayout) GetTicker() string { 20 | return "VTC" 21 | } 22 | 23 | func (p *VTCPayout) GetCoingeckoExchange() string { 24 | return "bittrex" 25 | } 26 | 27 | func (p *VTCPayout) GetCoingeckoCoinID() string { 28 | return "vertcoin" 29 | } 30 | 31 | func (p *VTCPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/bchpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &BCHPayout{} 4 | 5 | type BCHPayout struct{} 6 | 7 | func NewBCHPayout() *BCHPayout { 8 | return &BCHPayout{} 9 | } 10 | 11 | func (p *BCHPayout) GetID() int { 12 | return 5 13 | } 14 | 15 | func (p *BCHPayout) GetDisplayName() string { 16 | return "Bitcoin Cash" 17 | } 18 | 19 | func (p *BCHPayout) GetTicker() string { 20 | return "BCH" 21 | } 22 | 23 | func (p *BCHPayout) GetCoingeckoExchange() string { 24 | return "bittrex" 25 | } 26 | 27 | func (p *BCHPayout) GetCoingeckoCoinID() string { 28 | return "bitcoin-cash" 29 | } 30 | 31 | func (p *BCHPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/bnbpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &BNBPayout{} 4 | 5 | type BNBPayout struct{} 6 | 7 | func NewBNBPayout() *BNBPayout { 8 | return &BNBPayout{} 9 | } 10 | 11 | func (p *BNBPayout) GetID() int { 12 | return 27 13 | } 14 | 15 | func (p *BNBPayout) GetDisplayName() string { 16 | return "Binance Coin" 17 | } 18 | 19 | func (p *BNBPayout) GetTicker() string { 20 | return "BNB" 21 | } 22 | 23 | func (p *BNBPayout) GetCoingeckoExchange() string { 24 | return "binance" 25 | } 26 | 27 | func (p *BNBPayout) GetCoingeckoCoinID() string { 28 | return "binancecoin" 29 | } 30 | 31 | func (p *BNBPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/firopayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &FIROPayout{} 4 | 5 | type FIROPayout struct{} 6 | 7 | func NewFIROPayout() *FIROPayout { 8 | return &FIROPayout{} 9 | } 10 | 11 | func (p *FIROPayout) GetID() int { 12 | return 11 13 | } 14 | 15 | func (p *FIROPayout) GetDisplayName() string { 16 | return "Firo" 17 | } 18 | 19 | func (p *FIROPayout) GetTicker() string { 20 | return "FIRO" 21 | } 22 | 23 | func (p *FIROPayout) GetCoingeckoExchange() string { 24 | return "bittrex" 25 | } 26 | 27 | func (p *FIROPayout) GetCoingeckoCoinID() string { 28 | return "zcoin" 29 | } 30 | 31 | func (p *FIROPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/fluxpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &FLUXPayout{} 4 | 5 | type FLUXPayout struct{} 6 | 7 | func NewFLUXPayout() *FLUXPayout { 8 | return &FLUXPayout{} 9 | } 10 | 11 | func (p *FLUXPayout) GetID() int { 12 | return 13 13 | } 14 | 15 | func (p *FLUXPayout) GetDisplayName() string { 16 | return "Flux" 17 | } 18 | 19 | func (p *FLUXPayout) GetTicker() string { 20 | return "FLUX" 21 | } 22 | 23 | func (p *FLUXPayout) GetCoingeckoExchange() string { 24 | return "coinex" 25 | } 26 | 27 | func (p *FLUXPayout) GetCoingeckoCoinID() string { 28 | return "zelcash" 29 | } 30 | 31 | func (p *FLUXPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/grspayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &GRSPayout{} 4 | 5 | type GRSPayout struct{} 6 | 7 | func NewGRSPayout() *GRSPayout { 8 | return &GRSPayout{} 9 | } 10 | 11 | func (p *GRSPayout) GetID() int { 12 | return 10 13 | } 14 | 15 | func (p *GRSPayout) GetDisplayName() string { 16 | return "Groestlcoin" 17 | } 18 | 19 | func (p *GRSPayout) GetTicker() string { 20 | return "GRS" 21 | } 22 | 23 | func (p *GRSPayout) GetCoingeckoExchange() string { 24 | return "bittrex" 25 | } 26 | 27 | func (p *GRSPayout) GetCoingeckoCoinID() string { 28 | return "groestlcoin" 29 | } 30 | 31 | func (p *GRSPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/lbcpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &LBCPayout{} 4 | 5 | type LBCPayout struct{} 6 | 7 | func NewLBCPayout() *LBCPayout { 8 | return &LBCPayout{} 9 | } 10 | 11 | func (p *LBCPayout) GetID() int { 12 | return 15 13 | } 14 | 15 | func (p *LBCPayout) GetDisplayName() string { 16 | return "LBRY Credits" 17 | } 18 | 19 | func (p *LBCPayout) GetTicker() string { 20 | return "LBC" 21 | } 22 | 23 | func (p *LBCPayout) GetCoingeckoExchange() string { 24 | return "bittrex" 25 | } 26 | 27 | func (p *LBCPayout) GetCoingeckoCoinID() string { 28 | return "lbry-credits" 29 | } 30 | 31 | func (p *LBCPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/cakepayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &CAKEPayout{} 4 | 5 | type CAKEPayout struct{} 6 | 7 | func NewCAKEPayout() *CAKEPayout { 8 | return &CAKEPayout{} 9 | } 10 | 11 | func (p *CAKEPayout) GetID() int { 12 | return 28 13 | } 14 | 15 | func (p *CAKEPayout) GetDisplayName() string { 16 | return "Cake" 17 | } 18 | 19 | func (p *CAKEPayout) GetTicker() string { 20 | return "CAKE" 21 | } 22 | 23 | func (p *CAKEPayout) GetCoingeckoExchange() string { 24 | return "binance" 25 | } 26 | 27 | func (p *CAKEPayout) GetCoingeckoCoinID() string { 28 | return "pancakeswap-token" 29 | } 30 | 31 | func (p *CAKEPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/etcpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &ETCPayout{} 4 | 5 | type ETCPayout struct{} 6 | 7 | func NewETCPayout() *ETCPayout { 8 | return &ETCPayout{} 9 | } 10 | 11 | func (p *ETCPayout) GetID() int { 12 | return 21 13 | } 14 | 15 | func (p *ETCPayout) GetDisplayName() string { 16 | return "Ethereum Classic" 17 | } 18 | 19 | func (p *ETCPayout) GetTicker() string { 20 | return "ETC" 21 | } 22 | 23 | func (p *ETCPayout) GetCoingeckoExchange() string { 24 | return "bittrex" 25 | } 26 | 27 | func (p *ETCPayout) GetCoingeckoCoinID() string { 28 | return "ethereum-classic" 29 | } 30 | 31 | func (p *ETCPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/maticpayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &MATICPayout{} 4 | 5 | type MATICPayout struct{} 6 | 7 | func NewMATICPayout() *MATICPayout { 8 | return &MATICPayout{} 9 | } 10 | 11 | func (p *MATICPayout) GetID() int { 12 | return 25 13 | } 14 | 15 | func (p *MATICPayout) GetDisplayName() string { 16 | return "Polygon" 17 | } 18 | 19 | func (p *MATICPayout) GetTicker() string { 20 | return "MATIC" 21 | } 22 | 23 | func (p *MATICPayout) GetCoingeckoExchange() string { 24 | return "binance" 25 | } 26 | 27 | func (p *MATICPayout) GetCoingeckoCoinID() string { 28 | return "matic" 29 | } 30 | 31 | func (p *MATICPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/shibpayout.go: -------------------------------------------------------------------------------- 1 | 2 | package payouts 3 | 4 | var _ Payout = &SHIBPayout{} 5 | 6 | type SHIBPayout struct{} 7 | 8 | func NewSHIBPayout() *SHIBPayout { 9 | return &SHIBPayout{} 10 | } 11 | 12 | func (p *SHIBPayout) GetID() int { 13 | return 24 14 | } 15 | 16 | func (p *SHIBPayout) GetDisplayName() string { 17 | return "Shiba Inu" 18 | } 19 | 20 | func (p *SHIBPayout) GetTicker() string { 21 | return "SHIB" 22 | } 23 | 24 | func (p *SHIBPayout) GetCoingeckoExchange() string { 25 | return "binance" 26 | } 27 | 28 | func (p *SHIBPayout) GetCoingeckoCoinID() string { 29 | return "shiba-inu" 30 | } 31 | 32 | func (p *SHIBPayout) GetNetworks() []string { 33 | return []string{} 34 | } 35 | -------------------------------------------------------------------------------- /payouts/dogepayout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | var _ Payout = &DOGEPayout{} 4 | 5 | type DOGEPayout struct{} 6 | 7 | func NewDOGEPayout() *DOGEPayout { 8 | return &DOGEPayout{} 9 | } 10 | 11 | func (p *DOGEPayout) GetID() int { 12 | return 4 13 | } 14 | 15 | func (p *DOGEPayout) GetDisplayName() string { 16 | return "Verthash OCM Dogecoin Wallet" 17 | } 18 | 19 | func (p *DOGEPayout) GetTicker() string { 20 | return "DOGE" 21 | } 22 | 23 | func (p *DOGEPayout) GetCoingeckoExchange() string { 24 | return "bittrex" 25 | } 26 | 27 | func (p *DOGEPayout) GetCoingeckoCoinID() string { 28 | return "dogecoin" 29 | } 30 | 31 | func (p *DOGEPayout) GetNetworks() []string { 32 | return []string{} 33 | } 34 | -------------------------------------------------------------------------------- /payouts/usdtpayout.go: -------------------------------------------------------------------------------- 1 | 2 | package payouts 3 | 4 | var _ Payout = &USDTPayout{} 5 | 6 | type USDTPayout struct{} 7 | 8 | func NewUSDTPayout() *USDTPayout { 9 | return &USDTPayout{} 10 | } 11 | 12 | func (p *USDTPayout) GetID() int { 13 | return 23 14 | } 15 | 16 | func (p *USDTPayout) GetDisplayName() string { 17 | return "Tether" 18 | } 19 | 20 | func (p *USDTPayout) GetTicker() string { 21 | return "USDT" 22 | } 23 | 24 | func (p *USDTPayout) GetCoingeckoExchange() string { 25 | return "bittrex" 26 | } 27 | 28 | func (p *USDTPayout) GetCoingeckoCoinID() string { 29 | return "tether" 30 | } 31 | 32 | func (p *USDTPayout) GetNetworks() []string { 33 | return []string{ 34 | "ERC20", 35 | "TRC20", 36 | "BEP20", 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /backend/update.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/vertiond/verthash-one-click-miner/tracking" 7 | "github.com/vertiond/verthash-one-click-miner/util" 8 | ) 9 | 10 | func (m *Backend) UpdateAvailable() bool { 11 | r, _ := util.GetLatestRelease() 12 | 13 | lastVersion := util.VersionStringToNumeric(r.Tag) 14 | myVersion := util.VersionStringToNumeric(tracking.GetVersion()) 15 | 16 | return lastVersion > myVersion 17 | } 18 | 19 | func (m *Backend) VersionDetails() []string { 20 | r, _ := util.GetLatestRelease() 21 | return []string{r.Tag, r.Body, r.URL} 22 | } 23 | 24 | func (m *Backend) UpdateLoop() { 25 | for { 26 | stopUpdate := false 27 | select { 28 | case stopUpdate = <-m.stopUpdate: 29 | case <-time.After(time.Second * 15): 30 | } 31 | 32 | if stopUpdate { 33 | break 34 | } 35 | 36 | m.runtime.Events.Emit("updateAvailable", m.UpdateAvailable()) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 22 | -------------------------------------------------------------------------------- /frontend/vue.config.js: -------------------------------------------------------------------------------- 1 | let cssConfig = {}; 2 | 3 | if (process.env.NODE_ENV == "production") { 4 | cssConfig = { 5 | extract: { 6 | filename: "[name].css", 7 | chunkFilename: "[name].css" 8 | } 9 | }; 10 | } 11 | 12 | module.exports = { 13 | chainWebpack: config => { 14 | let limit = 9999999999999999; 15 | config.module 16 | .rule("images") 17 | .test(/\.(png|gif|jpg)(\?.*)?$/i) 18 | .use("url-loader") 19 | .loader("url-loader") 20 | .tap(options => Object.assign(options, { limit: limit })); 21 | config.module 22 | .rule("fonts") 23 | .test(/\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/i) 24 | .use("url-loader") 25 | .loader("url-loader") 26 | .options({ 27 | limit: limit 28 | }); 29 | }, 30 | css: cssConfig, 31 | configureWebpack: { 32 | output: { 33 | filename: "[name].js" 34 | }, 35 | optimization: { 36 | splitChunks: false 37 | } 38 | }, 39 | devServer: { 40 | disableHostCheck: true, 41 | host: "localhost" 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /util/gpus_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "log" 5 | "testing" 6 | ) 7 | 8 | func TestNvidia(t *testing.T) { 9 | 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)"} 10 | g := GetGPUsFromStrings(gpus) 11 | for i, gpu := range g { 12 | if gpu.Type != GPUTypeNVidia { 13 | log.Printf("Did not detect %s as NVIDIA!", gpus[i]) 14 | t.Fail() 15 | } 16 | } 17 | } 18 | 19 | func TestAMD(t *testing.T) { 20 | gpus := []string{"AMD something", "Radeon something", "Radeon (TM) RX 480 Graphics", "AMD Radeon(TM) R7 Graphics", "Radeon RX 480", "Radeon (TM) RX 560 Graphics"} 21 | g := GetGPUsFromStrings(gpus) 22 | for _, gpu := range g { 23 | if gpu.Type != GPUTypeAMD { 24 | t.Fail() 25 | } 26 | } 27 | } 28 | 29 | func TestInvalid(t *testing.T) { 30 | gpus := []string{"Intel Integrated Graphics"} 31 | g := GetGPUsFromStrings(gpus) 32 | for _, gpu := range g { 33 | if gpu.Type != GPUTypeOther { 34 | t.Fail() 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | #name: golangci-lint 2 | #on: 3 | # push: 4 | # branches: 5 | # - master 6 | # pull_request: 7 | # branches: 8 | # - master 9 | #permissions: 10 | # contents: read 11 | # # Optional: allow read access to pull request. Use with `only-new-issues` option. 12 | # # pull-requests: read 13 | #jobs: 14 | # golangci: 15 | # name: lint 16 | # runs-on: ubuntu-latest 17 | # steps: 18 | # - uses: actions/checkout@v2 19 | # - name: Setup Go 20 | # uses: actions/setup-go@v2 21 | # with: 22 | # go-version: '1.17.0' # The Go version to download (if necessary) and use. 23 | # 24 | # # Install all the dependencies 25 | # - name: Install dependencies 26 | # run: | 27 | # go mod tidy 28 | # - name: golangci-lint 29 | # uses: golangci/golangci-lint-action@v2 30 | # with: 31 | # # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version 32 | # version: v1.43.0 33 | # # Run build of the application 34 | # - name: Run build 35 | # run: go build . 36 | # -------------------------------------------------------------------------------- /pools/pool.go: -------------------------------------------------------------------------------- 1 | package pools 2 | 3 | import ( 4 | "github.com/vertiond/verthash-one-click-miner/payouts" 5 | ) 6 | 7 | type Pool interface { 8 | GetPayouts(testnet bool) []payouts.Payout 9 | GetPendingPayout(addr string) uint64 10 | GetStratumUrl() string 11 | GetPassword(payoutTicker string, network string) string 12 | GetName() string 13 | GetID() int 14 | GetFee() float64 15 | OpenBrowserPayoutInfo(addr string) 16 | } 17 | 18 | func GetPools(testnet bool) []Pool { 19 | // if testnet { 20 | // return []Pool{ 21 | // NewP2Proxy(), 22 | // } 23 | // } 24 | return []Pool{ 25 | NewZergpool(), 26 | Newzpool(), 27 | NewHashCryptos(), 28 | //NewP2Pool(), 29 | } 30 | } 31 | 32 | func GetPool(poolID int, testnet bool) Pool { 33 | pools := GetPools(testnet) 34 | for _, p := range pools { 35 | if p.GetID() == poolID { 36 | return p 37 | } 38 | } 39 | return pools[0] 40 | } 41 | 42 | func GetPayout(pool Pool, payoutID int, testnet bool) payouts.Payout { 43 | payouts := pool.GetPayouts(testnet) 44 | for _, p := range payouts { 45 | if p.GetID() == payoutID { 46 | return p 47 | } 48 | } 49 | return payouts[0] 50 | } 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2019-2021 Gert-Jaap Glasbergen 2 | Copyright (C) 2021-present Verthash OCM developers 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /networks/networks.go: -------------------------------------------------------------------------------- 1 | package networks 2 | 3 | type Network struct { 4 | Base58P2PKHVersion byte 5 | Base58P2SHVersion byte 6 | InsightURL string 7 | Bech32Prefix string 8 | P2ProxyStratum string 9 | P2ProxyURL string 10 | WalletDB string 11 | OCMBackend string 12 | BackendServers []string 13 | } 14 | 15 | var Active Network 16 | 17 | func SetNetwork(testnet bool) { 18 | if testnet { 19 | Active = Network{ 20 | Base58P2PKHVersion: 74, 21 | Base58P2SHVersion: 196, 22 | InsightURL: "https://vtc-insight-testnet.gertjaap.org/", 23 | Bech32Prefix: "tvtc", 24 | P2ProxyStratum: "stratum+tcp://p2proxy-testnet.gertjaap.org:9171", 25 | P2ProxyURL: "https://p2proxy-testnet.gertjaap.org/", 26 | WalletDB: "wallet-testnet.db", 27 | } 28 | } else { 29 | Active = Network{ 30 | Base58P2PKHVersion: 30, 31 | Base58P2SHVersion: 22, 32 | InsightURL: "https://sochain.com/", 33 | Bech32Prefix: "vtc", 34 | P2ProxyStratum: "stratum+tcp://p2proxy.vertcoin.org:9171", 35 | P2ProxyURL: "https://p2proxy.vertcoin.org/", 36 | WalletDB: "wallet-testnet.db", 37 | BackendServers: []string{ 38 | "https://kforg.javerity.com/", 39 | "https://ocmbackend.javerity.com/", 40 | "https://ocmbackend.mindcraftblocks.com/"}, 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /frontend/src/components/Update.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 45 | 46 | 47 | 58 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verthash_one_click_miner", 3 | "author": "vertion", 4 | "private": true, 5 | "scripts": { 6 | "preinstall": "npx npm-force-resolutions", 7 | "serve": "vue-cli-service serve", 8 | "build": "vue-cli-service build", 9 | "lint": "vue-cli-service lint" 10 | }, 11 | "dependencies": { 12 | "@wailsapp/runtime": "^1.0.10", 13 | "core-js": "^2.6.11", 14 | "lodash": "^4.17.21", 15 | "lodash.clonedeep": "^4.5.0", 16 | "lodash.defaultsdeep": "^4.6.1", 17 | "vue": "^2.6.11", 18 | "vue-i18n": "^8.15.3" 19 | }, 20 | "devDependencies": { 21 | "@vue/cli-service": "^4.5.15", 22 | "eslint": "^5.8.0", 23 | "eslint-plugin-vue": "^5.2.3", 24 | "eventsource-polyfill": "^0.9.6", 25 | "highlight.js": "^10.4.1", 26 | "vue-template-compiler": "^2.6.11", 27 | "webpack-hot-middleware": "^2.25.2" 28 | }, 29 | "eslintConfig": { 30 | "root": true, 31 | "env": { 32 | "node": true 33 | }, 34 | "extends": [ 35 | "plugin:vue/essential", 36 | "eslint:recommended" 37 | ], 38 | "rules": {}, 39 | "parserOptions": { 40 | "parser": "babel-eslint" 41 | } 42 | }, 43 | "postcss": { 44 | "plugins": { 45 | "autoprefixer": {} 46 | } 47 | }, 48 | "resolutions": { 49 | "minimist": "1.2.3", 50 | "mkdir": "0.5.3" 51 | }, 52 | "browserslist": [ 53 | "> 1%", 54 | "last 2 versions", 55 | "not ie <= 8" 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /backend/languages.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "github.com/cloudfoundry/jibber_jabber" 5 | "github.com/vertiond/verthash-one-click-miner/logging" 6 | "golang.org/x/text/language" 7 | ) 8 | 9 | var availableLanguages = []string{ 10 | // First language is default. So 11 | // alphabetical order except for 12 | // this one 13 | "en", 14 | "bg", 15 | "da", 16 | "de", 17 | "es", 18 | "fr", 19 | "hi", 20 | "hr", 21 | "it", 22 | "ja", 23 | "lt", 24 | "nl", 25 | "no", 26 | "pa", 27 | "pl", 28 | "pt", 29 | "ro", 30 | "ru", 31 | "sl", 32 | "sv", 33 | "tr", 34 | "zh", 35 | } 36 | 37 | var languageMatcher language.Matcher 38 | 39 | func init() { 40 | tags := []language.Tag{} 41 | for _, l := range availableLanguages { 42 | t, err := language.Parse(l) 43 | if err == nil { 44 | tags = append(tags, t) 45 | } 46 | } 47 | 48 | languageMatcher = language.NewMatcher(tags) 49 | } 50 | 51 | func (m *Backend) GetLocale() string { 52 | userLanguage, err := jibber_jabber.DetectIETF() 53 | if err != nil { 54 | logging.Warnf("Could not determine locale, defaulting to English: %s", err.Error()) 55 | return "en" 56 | } 57 | 58 | logging.Infof("User IETF is %s", userLanguage) 59 | userTag, err := language.Parse(userLanguage) 60 | if err != nil { 61 | logging.Warnf("Could not parse user IETF: %s", err.Error()) 62 | } 63 | tag, _, _ := languageMatcher.Match(userTag) 64 | logging.Infof("Matched tag is %s", tag.String()) 65 | base, _ := tag.Base() 66 | logging.Infof("Returning locale %s", base.String()) 67 | return base.String() 68 | } 69 | -------------------------------------------------------------------------------- /p2pool_nodes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Hostname": "mindcraftblocks.com", 4 | "Stratum": "stratum+tcp://mindcraftblocks.com:9171", 5 | "URL": "http://mindcraftblocks.com:9171/" 6 | }, 7 | { 8 | "Hostname": "vtc-fl.javerity.com", 9 | "Stratum": "stratum+tcp://vtc-fl.javerity.com:9171", 10 | "URL": "http://vtc-fl.javerity.com:9171/" 11 | }, 12 | { 13 | "Hostname": "vtc-ca.javerity.com", 14 | "Stratum": "stratum+tcp://vtc-ca.javerity.com:9171", 15 | "URL": "http://vtc-ca.javerity.com:9171/" 16 | }, 17 | { 18 | "Hostname": "p2p-usa.xyz", 19 | "Stratum": "stratum+tcp://p2p-usa.xyz:9171", 20 | "URL": "http://p2p-usa.xyz:9171/" 21 | }, 22 | { 23 | "Hostname": "p2p-ekb.xyz", 24 | "Stratum": "stratum+tcp://p2p-ekb.xyz:9171", 25 | "URL": "http://p2p-ekb.xyz:9171/" 26 | }, 27 | { 28 | "Hostname": "asia.p2p-spb.xyz", 29 | "Stratum": "stratum+tcp://asia.p2p-spb.xyz:9171", 30 | "URL": "http://asia.p2p-spb.xyz:9171/" 31 | }, 32 | { 33 | "Hostname": "p2p-spb.xyz", 34 | "Stratum": "stratum+tcp://p2p-spb.xyz:9171", 35 | "URL": "http://p2p-spb.xyz:9171/" 36 | }, 37 | { 38 | "Hostname": "p2p-south.xyz", 39 | "Stratum": "stratum+tcp://p2p-south.xyz:9171", 40 | "URL": "http://p2p-south.xyz:9171/" 41 | }, 42 | { 43 | "Hostname": "siberia.mine.nu", 44 | "Stratum": "stratum+tcp://siberia.mine.nu:9171", 45 | "URL": "http://siberia.mine.nu:9171/" 46 | }, 47 | { 48 | "Hostname": "boofpool.ddns.net", 49 | "Stratum": "stratum+tcp://boofpool.ddns.net:9171", 50 | "URL": "http://boofpool.ddns.net:9171/" 51 | }, 52 | { 53 | "Hostname": "vtc-brn.webhop.me", 54 | "Stratum": "stratum+tcp://vtc-brn.webhop.me:9171", 55 | "URL": "http://vtc-brn.webhop.me:9171/" 56 | }, 57 | { 58 | "Hostname": "159.223.184.20", 59 | "Stratum": "stratum+tcp://159.223.184.20:9171", 60 | "URL": "http://159.223.184.20:9171/static/" 61 | } 62 | ] 63 | -------------------------------------------------------------------------------- /util/versioncheck.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/vertiond/verthash-one-click-miner/logging" 9 | ) 10 | 11 | type GithubRelease struct { 12 | URL string `json:"html_url"` 13 | Tag string `json:"tag_name"` 14 | Body string `json:"body"` 15 | Draft bool `json:"draft"` 16 | Prerelease bool `json:"prerelease"` 17 | } 18 | 19 | var releases []GithubRelease 20 | 21 | func init() { 22 | err := GetJson("https://api.github.com/repos/vertiond/verthash-one-click-miner/releases", &releases) 23 | if err != nil { 24 | logging.Errorf("Error fetching releases: %v", err) 25 | } 26 | } 27 | 28 | func GetLatestRelease() (GithubRelease, error) { 29 | for _, r := range releases { 30 | if !r.Draft { 31 | return r, nil 32 | } 33 | } 34 | return GithubRelease{}, fmt.Errorf("No release found") 35 | } 36 | 37 | func VersionStringToNumeric(ver string) int64 { 38 | verNum := int64(0) 39 | // split off suffix 40 | suffix := "" 41 | suffixIdx := strings.Index(ver, "-") 42 | if suffixIdx > -1 { 43 | suffix = ver[suffixIdx+1:] 44 | ver = ver[:suffixIdx] 45 | 46 | // Chop off possible git commit hash and "-dirty" 47 | suffixIdx = strings.Index(suffix, "-") 48 | if suffixIdx > -1 { 49 | suffix = suffix[:suffixIdx] 50 | } 51 | } 52 | 53 | if len(suffix) > 0 { 54 | if strings.Contains(suffix, "alpha") { 55 | verNum += -999 56 | suffix = strings.ReplaceAll(suffix, "alpha", "") 57 | } 58 | if strings.Contains(suffix, "beta") { 59 | verNum += -899 60 | suffix = strings.ReplaceAll(suffix, "beta", "") 61 | } 62 | suffixVal, _ := strconv.Atoi(suffix) 63 | verNum += int64(suffixVal) 64 | } 65 | 66 | versionParts := strings.Split(ver, ".") 67 | multiplier := int64(100000000) 68 | for _, v := range versionParts { 69 | verVal, _ := strconv.Atoi(v) 70 | verNum += (int64(verVal) * multiplier) 71 | multiplier /= 100 72 | } 73 | 74 | return verNum 75 | } 76 | -------------------------------------------------------------------------------- /pools/hashcryptos.go: -------------------------------------------------------------------------------- 1 | package pools 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "time" 7 | 8 | "github.com/vertiond/verthash-one-click-miner/payouts" 9 | "github.com/vertiond/verthash-one-click-miner/util" 10 | ) 11 | 12 | var _ Pool = &HashCryptos{} 13 | 14 | type HashCryptos struct { 15 | LastFetchedPayout time.Time 16 | LastPayout uint64 17 | } 18 | 19 | func NewHashCryptos() *HashCryptos { 20 | return &HashCryptos{} 21 | } 22 | 23 | func (p *HashCryptos) GetPayouts(testnet bool) []payouts.Payout { 24 | if testnet { 25 | return []payouts.Payout{ 26 | payouts.NewVTCPayout(), 27 | } 28 | } 29 | return []payouts.Payout{ 30 | payouts.NewDOGEPayout(), 31 | payouts.NewVTCPayout(), 32 | payouts.NewBTCPayout(), 33 | payouts.NewBCHPayout(), 34 | payouts.NewDASHPayout(), 35 | payouts.NewDGBPayout(), 36 | payouts.NewFIROPayout(), 37 | payouts.NewGRSPayout(), 38 | payouts.NewLTCPayout(), 39 | payouts.NewXMRPayout(), 40 | payouts.NewRVNPayout(), 41 | } 42 | } 43 | 44 | func (p *HashCryptos) GetPendingPayout(addr string) uint64 { 45 | jsonPayload := map[string]interface{}{} 46 | err := util.GetJson(fmt.Sprintf("https://www.hashcryptos.com/api/walletEx/?address=%s", addr), &jsonPayload) 47 | if err != nil { 48 | return 0 49 | } 50 | unpaid, ok := jsonPayload["unpaid"].(string) 51 | if !ok { 52 | return 0 53 | } 54 | vtc, _ := strconv.ParseFloat(unpaid, 64) 55 | vtc *= 100000000 56 | return uint64(vtc) 57 | } 58 | 59 | func (p *HashCryptos) GetStratumUrl() string { 60 | return "stratum+tcp://stratum3.hashcryptos.com:9991" 61 | } 62 | 63 | func (p *HashCryptos) GetPassword(payoutTicker string, network string) string { 64 | return "x" 65 | } 66 | 67 | func (p *HashCryptos) GetID() int { 68 | return 6 69 | } 70 | 71 | func (p *HashCryptos) GetName() string { 72 | return "HashCryptos.com" 73 | } 74 | 75 | func (p *HashCryptos) GetFee() float64 { 76 | return 0.00 77 | } 78 | 79 | func (p *HashCryptos) OpenBrowserPayoutInfo(addr string) { 80 | util.OpenBrowser(fmt.Sprintf("https://hashcryptos.com/?address=%s", addr)) 81 | } 82 | -------------------------------------------------------------------------------- /pools/zpool.go: -------------------------------------------------------------------------------- 1 | package pools 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/vertiond/verthash-one-click-miner/payouts" 8 | "github.com/vertiond/verthash-one-click-miner/util" 9 | ) 10 | 11 | var _ Pool = &zpool{} 12 | 13 | type zpool struct { 14 | LastFetchedPayout time.Time 15 | LastPayout uint64 16 | } 17 | 18 | func Newzpool() *zpool { 19 | return &zpool{} 20 | } 21 | 22 | func (p *zpool) GetPayouts(testnet bool) []payouts.Payout { 23 | if testnet { 24 | return []payouts.Payout{ 25 | payouts.NewVTCPayout(), 26 | } 27 | } 28 | return []payouts.Payout{ 29 | payouts.NewDOGEPayout(), 30 | payouts.NewVTCPayout(), 31 | payouts.NewBTCPayout(), 32 | payouts.NewBCHPayout(), 33 | payouts.NewDASHPayout(), 34 | payouts.NewDGBPayout(), 35 | payouts.NewFLUXPayout(), 36 | payouts.NewGRSPayout(), 37 | payouts.NewZENPayout(), 38 | payouts.NewKMDPayout(), 39 | payouts.NewLBCPayout(), 40 | payouts.NewLTCPayout(), 41 | payouts.NewPPCPayout(), 42 | payouts.NewRVNPayout(), 43 | payouts.NewXVGPayout(), 44 | payouts.NewZECPayout(), 45 | } 46 | } 47 | 48 | func (p *zpool) GetPendingPayout(addr string) uint64 { 49 | jsonPayload := map[string]interface{}{} 50 | err := util.GetJson(fmt.Sprintf("https://zpool.ca/api/wallet?address=%s", addr), &jsonPayload) 51 | if err != nil { 52 | return 0 53 | } 54 | vtc, ok := jsonPayload["unpaid"].(float64) 55 | if !ok { 56 | return 0 57 | } 58 | vtc *= 100000000 59 | return uint64(vtc) 60 | } 61 | 62 | func (p *zpool) GetStratumUrl() string { 63 | return "stratum+tcp://verthash.mine.zpool.ca:6144" 64 | } 65 | 66 | func (p *zpool) GetPassword(payoutTicker string, network string) string { 67 | return fmt.Sprintf("c=%s,zap=VTC", payoutTicker) 68 | } 69 | 70 | func (p *zpool) GetID() int { 71 | return 7 72 | } 73 | 74 | func (p *zpool) GetName() string { 75 | return "Zpool.ca" 76 | } 77 | 78 | func (p *zpool) GetFee() float64 { 79 | return 0.50 80 | } 81 | 82 | func (p *zpool) OpenBrowserPayoutInfo(addr string) { 83 | util.OpenBrowser(fmt.Sprintf("https://zpool.ca/wallet/%s", addr)) 84 | } 85 | -------------------------------------------------------------------------------- /wallet/signsend.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "fmt" 7 | 8 | "github.com/btcsuite/btcd/btcec" 9 | "github.com/btcsuite/btcd/txscript" 10 | "github.com/btcsuite/btcd/wire" 11 | "github.com/vertiond/verthash-one-click-miner/keyfile" 12 | "github.com/vertiond/verthash-one-click-miner/networks" 13 | "github.com/vertiond/verthash-one-click-miner/util" 14 | ) 15 | 16 | // SignMyInputs finds the inputs in a transaction that came from our own wallet, and signs them with our private keys. 17 | // Will modify the transaction in place, but will ignore inputs that we can't sign and leave them unsigned. 18 | func (w *Wallet) SignMyInputs(tx *wire.MsgTx, password string) error { 19 | 20 | // For now using only P2PKH signing - since we generate 21 | // a legacy address. Will have to use segwit stuff at some point 22 | 23 | // generate tx-wide hashCache for segwit stuff 24 | // might not be needed (non-witness) but make it anyway 25 | // hCache := txscript.NewTxSigHashes(tx) 26 | 27 | // make the stashes for signatures / witnesses 28 | sigStash := make([][]byte, len(tx.TxIn)) 29 | 30 | // get key 31 | privBytes, err := keyfile.LoadPrivateKey(password) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), privBytes) 37 | 38 | for i := range tx.TxIn { 39 | sigStash[i], err = txscript.SignatureScript(tx, i, w.Script, txscript.SigHashAll, priv, true) 40 | if err != nil { 41 | return err 42 | } 43 | } 44 | // swap sigs into sigScripts in txins 45 | for i, txin := range tx.TxIn { 46 | if sigStash[i] != nil { 47 | txin.SignatureScript = sigStash[i] 48 | } 49 | } 50 | 51 | return nil 52 | } 53 | 54 | type txSend struct { 55 | RawTx string `json:"rawtx"` 56 | } 57 | 58 | type txSendReply struct { 59 | TxId string `json:"txid"` 60 | } 61 | 62 | func (w *Wallet) Send(tx *wire.MsgTx) (string, error) { 63 | var b bytes.Buffer 64 | err := tx.Serialize(&b) 65 | if err != nil { 66 | return "", err 67 | } 68 | s := txSend{ 69 | RawTx: hex.EncodeToString(b.Bytes()), 70 | } 71 | 72 | r := txSendReply{} 73 | 74 | err = util.PostJson(fmt.Sprintf("%stx", networks.Active.OCMBackend), s, &r) 75 | if err != nil { 76 | return "", err 77 | } 78 | 79 | return r.TxId, err 80 | } 81 | -------------------------------------------------------------------------------- /ADD-A-COIN.md: -------------------------------------------------------------------------------- 1 | # How to add a coin 2 | 3 | ### Requirements 4 | 5 | - Actively mined on [Zergpool.com](https://zergpool.com/site/exchangestatus) or [zpool.ca](https://www.zpool.ca/coins) 6 | - BTC price available on an exchange found in [Coingecko's API](https://api.coingecko.com/api/v3/exchanges/) 7 | - Submit a PR formatted in the following format 8 | 9 | ### What files must be created and edited? 10 | 11 | In the Payouts folder, a new file titled `btcpayout.go` is created replacing btc with the ticker of the coin you are adding. 12 | `````` 13 | package payouts 14 | 15 | var _ Payout = &BTCPayout{} 16 | 17 | type BTCPayout struct {} 18 | 19 | func NewBTCPayout() *BTCPayout { 20 | return &BTCPayout{} 21 | } 22 | 23 | func (p *BTCPayout) GetID() int { 24 | return 2 25 | } 26 | 27 | func (p *BTCPayout) GetDisplayName() string { 28 | return "Bitcoin" 29 | } 30 | 31 | func (p *BTCPayout) GetTicker() string { 32 | return "BTC" 33 | } 34 | 35 | func (p *BTCPayout) GetCoingeckoExchange() string { 36 | return "bittrex" 37 | } 38 | 39 | func (p *BTCPayout) GetCoingeckoCoinID() string { 40 | return "bitcoin" 41 | } 42 | `````` 43 | - Replace all instances of `BTC` in `BTCPayout` and `NewBTCPayout` with the ticker 44 | - Under `GetID` `return 2`, replace `2` with a new number that comes sequentially after the last coin added. See IDs listed under other coins in the payouts folder 45 | - Under `GetDisplayName` `return "Bitcoin"`, replace `Bitcoin` with the name that you wish to be displayed in the Mining Pool dropdown menu 46 | - Under `GetTicker` `return "BTC"`, replace `BTC` with the ticker used by Zergpool and/or zpool for miner configuration 47 | - Under `GetCoingeckoExchange` `return "bittrex"`, replace `bittrex` with the exact name of an exchange as used within the Coingecko API 48 | - Under `GetCoingeckoCoinID` `return "bitcoin"`, replace `bitcoin` with the name of the CoinID as used within the Coingecko API 49 | 50 | Finally, in the pools folder add the payout option in alphabetical order of GetDisplayName to zergpool.go and/or zpool.go under `return []payouts.Payout{` 51 | 52 | ### Coingecko API 53 | 54 | This software pulls an exchange rate for a BTC pair from the Coingecko API using this format for example: https://api.coingecko.com/api/v3/exchanges/bittrex/tickers?coin_ids=dogecoin 55 | 56 | `GetCoingeckoExchange` and `GetCoingeckoID` must match what would be in this URL 57 | -------------------------------------------------------------------------------- /pools/zergpool.go: -------------------------------------------------------------------------------- 1 | package pools 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/vertiond/verthash-one-click-miner/payouts" 8 | "github.com/vertiond/verthash-one-click-miner/util" 9 | ) 10 | 11 | var _ Pool = &Zergpool{} 12 | 13 | type Zergpool struct { 14 | LastFetchedPayout time.Time 15 | LastPayout uint64 16 | } 17 | 18 | func NewZergpool() *Zergpool { 19 | return &Zergpool{} 20 | } 21 | 22 | func (p *Zergpool) GetPayouts(testnet bool) []payouts.Payout { 23 | if testnet { 24 | return []payouts.Payout{ 25 | payouts.NewVTCPayout(), 26 | } 27 | } 28 | return []payouts.Payout{ 29 | payouts.NewDOGEPayout(), 30 | payouts.NewVTCPayout(), 31 | payouts.NewBTCPayout(), 32 | payouts.NewBCHPayout(), 33 | payouts.NewDASHPayout(), 34 | payouts.NewDGBPayout(), 35 | payouts.NewETCPayout(), 36 | payouts.NewFIROPayout(), 37 | payouts.NewFLUXPayout(), 38 | payouts.NewGRSPayout(), 39 | payouts.NewZENPayout(), 40 | payouts.NewKMDPayout(), 41 | payouts.NewLTCPayout(), 42 | payouts.NewXMRPayout(), 43 | payouts.NewMATICPayout(), 44 | payouts.NewRTMPayout(), 45 | payouts.NewRVNPayout(), 46 | payouts.NewSHIBPayout(), 47 | payouts.NewUSDTPayout(), 48 | payouts.NewTRXPayout(), 49 | payouts.NewBNBPayout(), 50 | payouts.NewCAKEPayout(), 51 | } 52 | } 53 | 54 | func (p *Zergpool) GetPendingPayout(addr string) uint64 { 55 | jsonPayload := map[string]interface{}{} 56 | err := util.GetJson(fmt.Sprintf("https://api.zergpool.com:8443/api/walletEx?address=%s", addr), &jsonPayload) 57 | if err != nil { 58 | return 0 59 | } 60 | vtc, ok := jsonPayload["unpaid"].(float64) 61 | if !ok { 62 | return 0 63 | } 64 | vtc *= 100000000 65 | return uint64(vtc) 66 | } 67 | 68 | func (p *Zergpool) GetStratumUrl() string { 69 | return "stratum+tcp://verthash.mine.zergpool.com:4534" 70 | } 71 | 72 | func (p *Zergpool) GetPassword(payoutTicker string, network string) string { 73 | return fmt.Sprintf("c=%s%s,mc=VTC", payoutTicker, network) 74 | } 75 | 76 | func (p *Zergpool) GetID() int { 77 | return 5 78 | } 79 | 80 | func (p *Zergpool) GetName() string { 81 | return "Zergpool.com" 82 | } 83 | 84 | func (p *Zergpool) GetFee() float64 { 85 | return 0.50 86 | } 87 | 88 | func (p *Zergpool) OpenBrowserPayoutInfo(addr string) { 89 | util.OpenBrowser(fmt.Sprintf("https://zergpool.com/?address=%s", addr)) 90 | } 91 | -------------------------------------------------------------------------------- /frontend/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import VueI18n from "vue-i18n"; 4 | import * as Wails from "@wailsapp/runtime" 5 | 6 | Vue.use(VueI18n); 7 | Vue.config.productionTip = false; 8 | Vue.config.devtools = true; 9 | 10 | // Import all locales 11 | import locale_bg from "./i18n/bg.json"; 12 | import locale_da from "./i18n/da.json"; 13 | import locale_de from "./i18n/de.json"; 14 | import locale_en from "./i18n/en.json"; 15 | import locale_es from "./i18n/es.json"; 16 | import locale_fr from "./i18n/fr.json"; 17 | import locale_hi from "./i18n/hi.json"; 18 | import locale_hr from "./i18n/hr.json"; 19 | import locale_it from "./i18n/it.json"; 20 | import locale_ja from "./i18n/ja.json"; 21 | import locale_lt from "./i18n/lt.json"; 22 | import locale_nl from "./i18n/nl.json"; 23 | import locale_no from "./i18n/no.json"; 24 | import locale_pa from "./i18n/pa.json"; 25 | import locale_pl from "./i18n/pl.json"; 26 | import locale_pt from "./i18n/pt.json"; 27 | import locale_ro from "./i18n/ro.json"; 28 | import locale_ru from "./i18n/ru.json"; 29 | import locale_sl from "./i18n/sl.json"; 30 | import locale_sv from "./i18n/sv.json"; 31 | import locale_tr from "./i18n/tr.json"; 32 | import locale_zh from "./i18n/zh.json"; 33 | 34 | Wails.Init(() => { 35 | window.backend.Backend.GetLocale().then(result => { 36 | 37 | const i18n = new VueI18n({ 38 | locale: result, // set locale 39 | fallbackLocale: 'en', 40 | messages: { 41 | bg: locale_bg, 42 | da: locale_da, 43 | de: locale_de, 44 | en: locale_en, 45 | es: locale_es, 46 | fr: locale_fr, 47 | hi: locale_hi, 48 | hr: locale_hr, 49 | it: locale_it, 50 | ja: locale_ja, 51 | lt: locale_lt, 52 | nl: locale_nl, 53 | no: locale_no, 54 | pa: locale_pa, 55 | pl: locale_pl, 56 | pt: locale_pt, 57 | ro: locale_ro, 58 | ru: locale_ru, 59 | sl: locale_sl, 60 | sv: locale_sv, 61 | tr: locale_tr, 62 | zh: locale_zh, 63 | }, 64 | }); 65 | 66 | new Vue({ 67 | i18n, 68 | render: h => h(App) 69 | }).$mount("#app"); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /util/autostart.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/ProtonMail/go-autostart" 8 | "github.com/vertiond/verthash-one-click-miner/logging" 9 | ) 10 | 11 | var app *autostart.App 12 | var fullPath string 13 | var oldFullPathFile string 14 | 15 | func init() { 16 | fullPath, _ = filepath.Abs(os.Args[0]) 17 | oldFullPath := "" 18 | oldFullPathFile = filepath.Join(DataDirectory(), "auto_start") 19 | if FileExists(oldFullPathFile) { 20 | oldFullPathBytes, err := os.ReadFile(oldFullPath) 21 | if err != nil { 22 | oldFullPath = string(oldFullPathBytes) 23 | } 24 | } 25 | app = &autostart.App{ 26 | Name: "verthash-ocm", 27 | DisplayName: "Verthash One-Click miner", 28 | Exec: []string{fullPath}, 29 | } 30 | 31 | if oldFullPath != "" && oldFullPath != fullPath { 32 | oldApp := &autostart.App{ 33 | Name: "verthash-ocm", 34 | DisplayName: "Verthash One-Click miner", 35 | Exec: []string{oldFullPath}, 36 | } 37 | if oldApp.IsEnabled() { 38 | // We enabled autostart on a different location. Move it to the current 39 | // full executable path. Disable it on the old path and move to the new. 40 | logging.Debugf("Autostart was enabled on a different location.\nOld location: %s\nNew location: %s\nMoving it.", oldFullPath, fullPath) 41 | err := oldApp.Disable() 42 | if err == nil { 43 | err := app.Enable() 44 | if err == nil { 45 | err = os.WriteFile(oldFullPathFile, []byte(fullPath), 0644) 46 | if err != nil { 47 | logging.Errorf("Writing outstart file failed: %v", err) 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | 55 | func GetAutoStart() bool { 56 | return app.IsEnabled() 57 | } 58 | 59 | func SetAutoStart(autoStart bool) string { 60 | if autoStart { 61 | err := app.Enable() 62 | if err != nil { 63 | return err.Error() 64 | } 65 | // Store the full path we created the autostart for, so we can 66 | // re-enable it on a new path when someone decides to download 67 | // an update to a different location. 68 | err = os.WriteFile(oldFullPathFile, []byte(fullPath), 0644) 69 | if err != nil { 70 | return err.Error() 71 | } 72 | } else { 73 | err := app.Disable() 74 | if err != nil { 75 | return err.Error() 76 | } 77 | err = os.Remove(oldFullPathFile) 78 | if err != nil { 79 | return err.Error() 80 | } 81 | } 82 | 83 | return "" 84 | } 85 | -------------------------------------------------------------------------------- /prerequisites/msvcrt2013.go: -------------------------------------------------------------------------------- 1 | package prerequisites 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "os" 10 | "os/exec" 11 | "path/filepath" 12 | "strings" 13 | 14 | "github.com/vertiond/verthash-one-click-miner/logging" 15 | "github.com/vertiond/verthash-one-click-miner/util" 16 | ) 17 | 18 | func vcrt2013Installed() bool { 19 | searchPath := strings.Split(os.Getenv("PATH"), ";") 20 | for _, p := range searchPath { 21 | search := filepath.Join(p, "MSVCR120.dll") 22 | if _, err := os.Stat(search); !os.IsNotExist(err) { 23 | return true 24 | } 25 | } 26 | logging.Infof("Visual C++ Redistributable is not found\n") 27 | return false 28 | } 29 | 30 | func installVCRT2013(install chan bool) error { 31 | if vcrt2013Installed() { 32 | return nil 33 | } 34 | 35 | install <- true 36 | 37 | err := downloadVCRT2013() 38 | if err != nil { 39 | return err 40 | } 41 | 42 | err = checkVCRT2013Hash() 43 | if err != nil { 44 | return err 45 | } 46 | 47 | installer := exec.Command(vcrt2013DownloadPath(), "/q", "/norestart") 48 | err = installer.Run() 49 | install <- false 50 | return err 51 | } 52 | 53 | func vcrt2013DownloadPath() string { 54 | downloadDir := filepath.Join(util.DataDirectory(), "prerequisites") 55 | downloadPath := filepath.Join(downloadDir, "vcredist_x64.exe") 56 | err := os.MkdirAll(downloadDir, 0755) 57 | if err != nil && !os.IsExist(err) { 58 | logging.Errorf("Could not create download dir for msvcrt: %v", err) 59 | } 60 | return downloadPath 61 | } 62 | 63 | func checkVCRT2013Hash() error { 64 | expectedHash, _ := hex.DecodeString("e554425243e3e8ca1cd5fe550db41e6fa58a007c74fad400274b128452f38fb8") 65 | realHash, err := util.ShaSum(vcrt2013DownloadPath()) 66 | if err != nil { 67 | return err 68 | } 69 | if !bytes.Equal(realHash, expectedHash) { 70 | return fmt.Errorf("Hash of downloaded VCRT runtime installer does not match") 71 | } 72 | return nil 73 | } 74 | 75 | func downloadVCRT2013() error { 76 | 77 | resp, err := http.Get("https://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x64.exe") 78 | if err != nil { 79 | return err 80 | } 81 | defer resp.Body.Close() 82 | 83 | // Create the file 84 | out, err := os.Create(vcrt2013DownloadPath()) 85 | if err != nil { 86 | return err 87 | } 88 | defer out.Close() 89 | 90 | // Write the body to file 91 | _, err = io.Copy(out, resp.Body) 92 | return err 93 | } 94 | -------------------------------------------------------------------------------- /frontend/src/components/TabBar.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 48 | 49 | 50 | 105 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/vertiond/verthash-one-click-miner 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a 7 | github.com/btcsuite/btcd v0.0.0-20190614013741-962a206e94e9 8 | github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d 9 | github.com/btcsuite/fastsha256 v0.0.0-20160815193821-637e65642941 10 | github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 11 | github.com/gertjaap/verthash-go v0.0.0-20210205201258-234a3a9698d1 12 | github.com/go-ping/ping v0.0.0-20210402232549-1726e5ede5b6 13 | github.com/marcsauter/single v0.0.0-20181104081128-f8bf46f26ec0 14 | github.com/tidwall/buntdb v1.2.10 15 | github.com/wailsapp/wails v1.16.9 16 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 17 | golang.org/x/text v0.3.6 18 | ) 19 | 20 | require ( 21 | github.com/Masterminds/semver v1.5.0 // indirect 22 | github.com/abadojack/whatlanggo v1.0.1 // indirect 23 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect 24 | github.com/fatih/color v1.12.0 // indirect 25 | github.com/go-playground/colors v1.2.0 // indirect 26 | github.com/gorilla/websocket v1.4.2 // indirect 27 | github.com/jackmordaunt/icns v1.0.0 // indirect 28 | github.com/kennygrant/sanitize v1.2.4 // indirect 29 | github.com/leaanthony/slicer v1.5.0 // indirect 30 | github.com/leaanthony/spinner v0.5.3 // indirect 31 | github.com/leaanthony/synx v0.1.0 // indirect 32 | github.com/leaanthony/wincursor v0.1.0 // indirect 33 | github.com/mattn/go-colorable v0.1.8 // indirect 34 | github.com/mattn/go-isatty v0.0.13 // indirect 35 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect 36 | github.com/pkg/browser v0.0.0-20210606212950-a7b7a6107d32 // indirect 37 | github.com/pkg/errors v0.9.1 // indirect 38 | github.com/sirupsen/logrus v1.8.1 // indirect 39 | github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba // indirect 40 | github.com/tidwall/btree v1.4.2 // indirect 41 | github.com/tidwall/gjson v1.14.3 // indirect 42 | github.com/tidwall/grect v0.1.4 // indirect 43 | github.com/tidwall/match v1.1.1 // indirect 44 | github.com/tidwall/pretty v1.2.0 // indirect 45 | github.com/tidwall/rtred v0.1.2 // indirect 46 | github.com/tidwall/tinyqueue v0.1.1 // indirect 47 | golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9 // indirect 48 | golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect 49 | golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect 50 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect 51 | ) 52 | -------------------------------------------------------------------------------- /wallet_doge/signsend.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "fmt" 7 | 8 | "github.com/btcsuite/btcd/btcec" 9 | "github.com/btcsuite/btcd/txscript" 10 | "github.com/btcsuite/btcd/wire" 11 | "github.com/vertiond/verthash-one-click-miner/keyfile" 12 | "github.com/vertiond/verthash-one-click-miner/networks" 13 | "github.com/vertiond/verthash-one-click-miner/util" 14 | ) 15 | 16 | // SignMyInputs finds the inputs in a transaction that came from our own wallet, and signs them with our private keys. 17 | // Will modify the transaction in place, but will ignore inputs that we can't sign and leave them unsigned. 18 | func (w *Wallet) SignMyInputs(tx *wire.MsgTx, password string) error { 19 | 20 | // For now using only P2PKH signing - since we generate 21 | // a legacy address. Will have to use segwit stuff at some point 22 | 23 | // generate tx-wide hashCache for segwit stuff 24 | // might not be needed (non-witness) but make it anyway 25 | // hCache := txscript.NewTxSigHashes(tx) 26 | 27 | // make the stashes for signatures / witnesses 28 | sigStash := make([][]byte, len(tx.TxIn)) 29 | 30 | // get key 31 | privBytes, err := keyfile.LoadPrivateKey(password) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), privBytes) 37 | 38 | for i := range tx.TxIn { 39 | sigStash[i], err = txscript.SignatureScript(tx, i, w.Script, txscript.SigHashAll, priv, true) 40 | if err != nil { 41 | return err 42 | } 43 | } 44 | // swap sigs into sigScripts in txins 45 | for i, txin := range tx.TxIn { 46 | if sigStash[i] != nil { 47 | txin.SignatureScript = sigStash[i] 48 | } 49 | } 50 | 51 | return nil 52 | } 53 | 54 | type txSend struct { 55 | RawTx string `json:"tx_hex"` 56 | } 57 | 58 | type txSendReply struct { 59 | TxId string `json:"txid"` 60 | } 61 | 62 | func (w *Wallet) Send(tx *wire.MsgTx) (string, error) { 63 | var b bytes.Buffer 64 | tx.Serialize(&b) 65 | s := txSend{ 66 | RawTx: hex.EncodeToString(b.Bytes()), 67 | } 68 | 69 | r := txSendReply{} 70 | 71 | jsonPayload := map[string]interface{}{} 72 | err := util.PostJson(fmt.Sprintf("%sapi/v2/send_tx/DOGE", networks.Active.InsightURL), s, &jsonPayload) 73 | json_parse_success := false 74 | if err == nil { 75 | jsonData, ok := jsonPayload["data"].(map[string]interface{}) 76 | if ok { 77 | txid, ok := jsonData["txid"].(string) 78 | r = txSendReply{txid} 79 | if ok { 80 | json_parse_success = true 81 | } 82 | } 83 | } 84 | 85 | if !json_parse_success { 86 | return "", err 87 | } 88 | 89 | return r.TxId, err 90 | } 91 | -------------------------------------------------------------------------------- /frontend/src/components/Tracking.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 72 | 73 | 74 | 96 | -------------------------------------------------------------------------------- /pools/p2pool.go: -------------------------------------------------------------------------------- 1 | package pools 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/vertiond/verthash-one-click-miner/logging" 8 | 9 | "github.com/vertiond/verthash-one-click-miner/payouts" 10 | "github.com/vertiond/verthash-one-click-miner/ping" 11 | "github.com/vertiond/verthash-one-click-miner/util" 12 | ) 13 | 14 | var _ Pool = &P2Pool{} 15 | 16 | type P2Pool struct { 17 | LastFetchedPayout time.Time 18 | LastPayout uint64 19 | LastFetchedFee time.Time 20 | LastFee float64 21 | } 22 | 23 | func NewP2Pool() *P2Pool { 24 | return &P2Pool{} 25 | } 26 | 27 | func (p *P2Pool) GetPayouts(testnet bool) []payouts.Payout { 28 | if testnet { 29 | return []payouts.Payout{ 30 | payouts.NewVTCPayout(), 31 | } 32 | } 33 | return []payouts.Payout{ 34 | } 35 | } 36 | 37 | func (p *P2Pool) GetPendingPayout(addr string) uint64 { 38 | if time.Since(p.LastFetchedPayout) > time.Minute*2 { 39 | jsonPayload := map[string]interface{}{} 40 | err := util.GetJson(fmt.Sprintf("%scurrent_payouts", ping.Selected.P2PoolURL), &jsonPayload) 41 | if err != nil { 42 | logging.Warnf("Unable to fetch p2pool payouts: %s", err.Error()) 43 | p.LastPayout = 0 44 | } 45 | address := addr 46 | vtc, ok := jsonPayload[address].(float64) 47 | if !ok { 48 | p.LastFetchedPayout = time.Now() 49 | p.LastPayout = 0 50 | } 51 | vtc *= 100000000 52 | p.LastFetchedPayout = time.Now() 53 | p.LastPayout = uint64(vtc) 54 | } 55 | return p.LastPayout 56 | } 57 | 58 | func (p *P2Pool) GetStratumUrl() string { 59 | return ping.Selected.P2PoolStratum 60 | } 61 | 62 | func (p *P2Pool) GetPassword(payoutTicker string, network string) string { 63 | return "x" 64 | } 65 | 66 | func (p *P2Pool) GetID() int { 67 | return 2 68 | } 69 | 70 | func (p *P2Pool) GetName() string { 71 | return "P2Pool" 72 | } 73 | 74 | func (p *P2Pool) GetFee() (fee float64) { 75 | if time.Since(p.LastFetchedFee) > time.Minute*30 { 76 | jsonPayload := map[string]interface{}{} 77 | err := util.GetJson(fmt.Sprintf("%slocal_stats", ping.Selected.P2PoolURL), &jsonPayload) 78 | if err != nil { 79 | logging.Warnf("Unable to fetch p2pool fee: %s", err.Error()) 80 | fee = 2.0 81 | } 82 | fee, ok := jsonPayload["fee"].(float64) 83 | if !ok { 84 | fee = 2.0 85 | return fee 86 | } 87 | donationFee, ok := jsonPayload["donation_proportion"].(float64) 88 | if !ok { 89 | fee = 2.0 90 | return fee 91 | } 92 | fee += donationFee 93 | p.LastFetchedFee = time.Now() 94 | p.LastFee = float64(fee) 95 | } 96 | return p.LastFee 97 | } 98 | 99 | func (p *P2Pool) OpenBrowserPayoutInfo(addr string) { 100 | util.OpenBrowser(ping.Selected.P2PoolURL) 101 | } 102 | -------------------------------------------------------------------------------- /frontend/src/i18n/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "generic" : { 3 | "retry" : "再试一次", 4 | "back_to_wallet" : "返回钱包", 5 | "close" : "关闭" 6 | }, 7 | "welcome" : { 8 | "alreadyrunning" : "一键式矿工《One-Click Miner》 已经在运行。你不能多次运行它。", 9 | "click_button_to_start" : "点击下面的按钮再次开始挖矿。", 10 | "startmining" : "开始挖矿!", 11 | "makeapassword" : "请输入密码。不要丢失你的密码。", 12 | "password" : "密码", 13 | "confirmpassword" : "确认密码", 14 | "password_cannot_be_empty" : "请输入密码", 15 | "password_mismatch" : "密码不一致", 16 | "error_initializing" : "初始化中发生了故障" 17 | }, 18 | "checks" : { 19 | "prerequisite" : "先行程序已运行,你可能看到弹窗弹出询问权限(可能只在任务栏中闪动)", 20 | "checks_failed" : "检查失败", 21 | "checking_mining_software" : "检查挖矿软件。。。", 22 | "rapidfail" : "检查是否发生快速失败", 23 | "compatibility" : "检查GPU兼容性。。。", 24 | "installing_miners" : "安装挖矿软件。。。" 25 | }, 26 | "mining" : { 27 | "spendable_balance" : "可花费余额", 28 | "still_maturing" : "还在成熟中", 29 | "pending_pool_payout" : "等待矿池支出", 30 | "waiting_for_miners" : "等待矿工开始", 31 | "expected_earnings_24h" : "预期收益(24小时)", 32 | "estimating" : "估计中", 33 | "stop_mining" : "停止挖矿" 34 | }, 35 | "sending" : { 36 | "send_all_to" : "发送你所有挖矿的币到", 37 | "youre_sending_x_to" : "你正在发送 {receivedBalance} 给", 38 | "youre_sending_x_in_y_txs_to" : "你将在 {receivedTxCount} 的交易中发送 {receivedBalance}", 39 | "receiver_address" : "接收地址", 40 | "wallet_password" : "钱包密码", 41 | "send" : "发送", 42 | "coins_sent" : "你的币被发送了!", 43 | "view_trans_plural" : "查看交易信息", 44 | "view_trans_singular" : "查看交易信息", 45 | "failed_to_send" : "发送币失败了", 46 | "password_required" : "请输入钱包密码", 47 | "invalid_address" : "无效地址", 48 | "script_failure" : "脚本失败", 49 | "could_not_calculate_fee" : "无法计算费用", 50 | "insufficient_funds" : "不足资金", 51 | "sign_failed":"无法签署交易。 检查密码", 52 | "send_failed" : "无法发送交易。查看debug.log以获取更多的信息" 53 | }, 54 | "settings" : { 55 | "enable_debug" : "启用调试", 56 | "enable_debug_sub" : "在调试日志中包含矿工的控制台输出。可以使你的日志变得很大。", 57 | "auto_start" : "自动开始", 58 | "auto_start_sub" : "登陆电脑时启动一键式矿工《One-Click Miner》", 59 | "closed_source" : "使用闭源的矿工", 60 | "closed_source_sub" : "更好的哈希率,但是未经审核的矿工会产生开发人员的费用", 61 | "closed_source_warning" : "你选择使用闭源的矿工。绿币(VTC)不支持这些矿工。他们无法对其内容进行审核并且可能包含损害你的电脑的功能。", 62 | "save_n_restart" : "保存及重启" 63 | }, 64 | "tabbar" : { 65 | "wallet" : "钱包", 66 | "send_coins" : "发送币 (DOGE)", 67 | "settings" : "设置" 68 | }, 69 | "tracking": { 70 | "update_available" : "更新可用", 71 | "tracking_enabled" : "你是匿名共享使用情况统计信息", 72 | "disable_tracking" : "停用", 73 | "tracking_disabled" : "你没有共享使用情况统计信息", 74 | "enable_tracking" : "启用这些可以帮我们改善你的应用体验", 75 | "report_issue" : "报告问题" 76 | }, 77 | "update" : { 78 | "new_version_available" : "新版本可用", 79 | "download" : "下载" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /frontend/src/components/Welcome.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 95 | 96 | 97 | 102 | -------------------------------------------------------------------------------- /miners/ccminer.go: -------------------------------------------------------------------------------- 1 | package miners 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | "sync" 7 | 8 | "github.com/vertiond/verthash-one-click-miner/logging" 9 | ) 10 | 11 | // Compile time assertion on interface 12 | var _ MinerImpl = &CCMinerImpl{} 13 | 14 | type CCMinerImpl struct { 15 | binaryRunner *BinaryRunner 16 | hashRates map[int64]uint64 17 | hashRatesLock sync.Mutex 18 | gpuCount int8 19 | } 20 | 21 | func NewCCMinerImpl(br *BinaryRunner) MinerImpl { 22 | return &CCMinerImpl{binaryRunner: br, hashRates: map[int64]uint64{}, hashRatesLock: sync.Mutex{}} 23 | } 24 | 25 | func (l *CCMinerImpl) Configure(args BinaryArguments) error { 26 | return nil 27 | } 28 | 29 | func (l *CCMinerImpl) ParseOutput(line string) { 30 | if l.binaryRunner.Debug { 31 | logging.Debugf("[ccminer] %s\n", line) 32 | } 33 | line = strings.TrimSpace(line) 34 | 35 | if strings.Contains(line, "GPU #") && strings.HasSuffix(line, ")") { 36 | startCountIdx := strings.Index(line, "GPU #") + 5 37 | gpuCountString := line[startCountIdx : startCountIdx+1] 38 | gpuCount64, _ := strconv.ParseInt(gpuCountString, 10, 8) 39 | l.gpuCount = int8(gpuCount64) + 1 40 | logging.Debugf("Set GPU Count to %d", l.gpuCount) 41 | } 42 | 43 | if strings.Contains(line, "GPU #") && strings.HasSuffix(line, "H/s") { 44 | startDeviceIdx := strings.Index(line, "GPU #") 45 | endDeviceIdx := strings.Index(line[startDeviceIdx:], ":") 46 | deviceIdxString := line[startDeviceIdx+5 : startDeviceIdx+endDeviceIdx] 47 | deviceIdx, err := strconv.ParseInt(deviceIdxString, 10, 64) 48 | if err != nil { 49 | return 50 | } 51 | 52 | startMHs := strings.LastIndex(line, ", ") 53 | if startMHs > -1 { 54 | hashRateUnit := strings.ToUpper(line[len(line)-4 : len(line)-3]) 55 | line = line[startMHs+2 : len(line)-5] 56 | f, err := strconv.ParseFloat(line, 64) 57 | if err != nil { 58 | logging.Errorf("Error parsing hashrate: %s\n", err.Error()) 59 | } 60 | if hashRateUnit == "K" { 61 | f = f * 1000 62 | } else if hashRateUnit == "M" { 63 | f = f * 1000 * 1000 64 | } else if hashRateUnit == "G" { 65 | f = f * 1000 * 1000 * 1000 66 | } 67 | 68 | l.hashRatesLock.Lock() 69 | l.hashRates[deviceIdx] = uint64(f) 70 | l.hashRatesLock.Unlock() 71 | } 72 | } 73 | } 74 | 75 | func (l *CCMinerImpl) HashRate() uint64 { 76 | totalHash := uint64(0) 77 | l.hashRatesLock.Lock() 78 | for _, h := range l.hashRates { 79 | totalHash += h 80 | } 81 | l.hashRatesLock.Unlock() 82 | return totalHash 83 | } 84 | 85 | func (l *CCMinerImpl) ConstructCommandlineArgs(args BinaryArguments) []string { 86 | return []string{"--max-log-rate", "0", "--no-color", "-a", "lyra2v3", "-o", args.StratumUrl, "-u", args.StratumUsername, "-p", args.StratumPassword} 87 | } 88 | 89 | func (l *CCMinerImpl) AvailableGPUs() int8 { 90 | err := l.binaryRunner.launch([]string{"-n"}, false) 91 | if err != nil { 92 | return 0 93 | } 94 | err = l.binaryRunner.cmd.Wait() 95 | if err != nil { 96 | return 0 97 | } 98 | // Output is caught by ParseOuput function above and this will set the gpuCount accordingly 99 | return l.gpuCount 100 | } 101 | -------------------------------------------------------------------------------- /backend/backend.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "path/filepath" 5 | 6 | "github.com/tidwall/buntdb" 7 | 8 | "github.com/btcsuite/btcd/wire" 9 | "github.com/vertiond/verthash-one-click-miner/miners" 10 | "github.com/vertiond/verthash-one-click-miner/payouts" 11 | "github.com/vertiond/verthash-one-click-miner/pools" 12 | "github.com/vertiond/verthash-one-click-miner/util" 13 | "github.com/vertiond/verthash-one-click-miner/wallet_doge" 14 | "github.com/wailsapp/wails" 15 | ) 16 | 17 | type Backend struct { 18 | runtime *wails.Runtime 19 | wal *wallet.Wallet 20 | settings *buntdb.DB 21 | pendingSweep []*wire.MsgTx 22 | minerBinaries []*miners.BinaryRunner 23 | rapidFailures []*miners.BinaryRunner 24 | pool pools.Pool 25 | payout payouts.Payout 26 | network string 27 | walletAddress string 28 | customAddress string 29 | refreshBalanceChan chan bool 30 | refreshHashChan chan bool 31 | refreshRunningState chan bool 32 | stopMonitoring chan bool 33 | stopHash chan bool 34 | stopBalance chan bool 35 | stopUpdate chan bool 36 | stopRunningState chan bool 37 | prerequisiteInstall chan bool 38 | alreadyRunning bool 39 | } 40 | 41 | func NewBackend(alreadyRunning bool) (*Backend, error) { 42 | backend := &Backend{ 43 | refreshBalanceChan: make(chan bool), 44 | refreshHashChan: make(chan bool), 45 | refreshRunningState: make(chan bool), 46 | stopHash: make(chan bool), 47 | stopBalance: make(chan bool), 48 | stopRunningState: make(chan bool), 49 | stopMonitoring: make(chan bool), 50 | stopUpdate: make(chan bool), 51 | prerequisiteInstall: make(chan bool), 52 | minerBinaries: []*miners.BinaryRunner{}, 53 | rapidFailures: []*miners.BinaryRunner{}, 54 | } 55 | 56 | if alreadyRunning { 57 | backend.alreadyRunning = true 58 | return backend, nil 59 | } 60 | 61 | db, err := buntdb.Open(filepath.Join(util.DataDirectory(), "settings.db")) 62 | if err != nil { 63 | return nil, err 64 | } 65 | backend.settings = db 66 | return backend, nil 67 | } 68 | 69 | func (m *Backend) ResetPool() { 70 | m.pool = pools.GetPool(m.GetPool(), m.GetTestnet()) 71 | } 72 | 73 | func (m *Backend) ResetPayout() { 74 | m.payout = pools.GetPayout(m.pool, m.GetPayout(), m.GetTestnet()) 75 | } 76 | 77 | func (m *Backend) ResetNetwork() { 78 | m.network = m.GetNetwork() 79 | } 80 | 81 | func (m *Backend) ResetCustomAddress() { 82 | m.customAddress = m.GetCustomAddress() 83 | } 84 | 85 | func (m *Backend) ResetWalletAddress() { 86 | m.walletAddress = m.Address() 87 | } 88 | 89 | func (m *Backend) WailsInit(runtime *wails.Runtime) error { 90 | // Save runtime 91 | m.runtime = runtime 92 | 93 | go m.PrerequisiteProxyLoop() 94 | go m.UpdateLoop() 95 | 96 | return nil 97 | } 98 | 99 | func (m *Backend) OpenDownloadUrl(url string) { 100 | util.OpenBrowser(url) 101 | } 102 | 103 | func (m *Backend) AlreadyRunning() bool { 104 | return m.alreadyRunning 105 | } 106 | 107 | func (m *Backend) Close() { 108 | m.runtime.Window.Close() 109 | } 110 | -------------------------------------------------------------------------------- /frontend/src/components/Checks.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 73 | 74 | 75 | 108 | 109 | -------------------------------------------------------------------------------- /miners/teamredminer.go: -------------------------------------------------------------------------------- 1 | package miners 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | "sync" 7 | 8 | "github.com/vertiond/verthash-one-click-miner/logging" 9 | ) 10 | 11 | // Compile time assertion on interface 12 | var _ MinerImpl = &TeamRedMinerImpl{} 13 | 14 | type TeamRedMinerImpl struct { 15 | binaryRunner *BinaryRunner 16 | hashRates map[int64]uint64 17 | hashRatesLock sync.Mutex 18 | gpuCount int8 19 | } 20 | 21 | func NewTeamRedMinerImpl(br *BinaryRunner) MinerImpl { 22 | return &TeamRedMinerImpl{binaryRunner: br, hashRates: map[int64]uint64{}, hashRatesLock: sync.Mutex{}} 23 | } 24 | 25 | func (l *TeamRedMinerImpl) Configure(args BinaryArguments) error { 26 | return nil 27 | } 28 | 29 | func (l *TeamRedMinerImpl) ParseOutput(line string) { 30 | if l.binaryRunner.Debug { 31 | logging.Debugf("[teamRedMiner] %s\n", line) 32 | } 33 | line = strings.TrimSpace(line) 34 | if strings.Contains(line, "] Detected") && strings.Contains(line, "devices, ") { 35 | startCountIdx := strings.Index(line, "] Detected ") + 11 36 | gpuCountString := line[startCountIdx : startCountIdx+1] 37 | logging.Debugf("GPUCountString: %s", gpuCountString) 38 | gpuCount64, _ := strconv.ParseInt(gpuCountString, 10, 8) 39 | l.gpuCount = int8(gpuCount64) 40 | logging.Debugf("Set GPU Count to %d", l.gpuCount) 41 | } 42 | if strings.Contains(line, "] GPU ") && strings.Contains(line, "lyra2rev3") { 43 | startDeviceIdx := strings.Index(line, "] GPU ") 44 | endDeviceIdx := strings.Index(line[startDeviceIdx:], "[") 45 | deviceIdxString := line[startDeviceIdx+6 : startDeviceIdx+endDeviceIdx-1] 46 | deviceIdx, err := strconv.ParseInt(deviceIdxString, 10, 64) 47 | if err != nil { 48 | return 49 | } 50 | 51 | startMHs := strings.Index(line, "lyra2rev3: ") 52 | if startMHs > -1 { 53 | endMHs := strings.Index(line[startMHs:], "h/s") 54 | hashRateUnit := strings.ToUpper(line[startMHs+endMHs-1 : startMHs+endMHs]) 55 | line = line[startMHs+11 : startMHs+endMHs-1] 56 | f, err := strconv.ParseFloat(line, 64) 57 | if err != nil { 58 | logging.Errorf("Error parsing hashrate: %s\n", err.Error()) 59 | } 60 | if hashRateUnit == "K" { 61 | f = f * 1000 62 | } else if hashRateUnit == "M" { 63 | f = f * 1000 * 1000 64 | } else if hashRateUnit == "G" { 65 | f = f * 1000 * 1000 * 1000 66 | } 67 | l.hashRatesLock.Lock() 68 | l.hashRates[deviceIdx] = uint64(f) 69 | l.hashRatesLock.Unlock() 70 | } 71 | } 72 | } 73 | 74 | func (l *TeamRedMinerImpl) HashRate() uint64 { 75 | totalHash := uint64(0) 76 | l.hashRatesLock.Lock() 77 | for _, h := range l.hashRates { 78 | totalHash += h 79 | } 80 | l.hashRatesLock.Unlock() 81 | return totalHash 82 | } 83 | 84 | func (l *TeamRedMinerImpl) ConstructCommandlineArgs(args BinaryArguments) []string { 85 | return []string{"--log_interval=10", "--disable_colors", "-a", "lyra2rev3", "-o", args.StratumUrl, "-u", args.StratumUsername, "-p", args.StratumPassword} 86 | } 87 | 88 | func (l *TeamRedMinerImpl) AvailableGPUs() int8 { 89 | err := l.binaryRunner.launch([]string{"--list_devices"}, false) 90 | if err != nil { 91 | return 0 92 | } 93 | err = l.binaryRunner.cmd.Wait() 94 | if err != nil { 95 | return 0 96 | } 97 | // Output is caught by ParseOuput function above and this will set the gpuCount accordingly 98 | return l.gpuCount 99 | } 100 | -------------------------------------------------------------------------------- /miners/cryptodredge.go: -------------------------------------------------------------------------------- 1 | package miners 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | "sync" 7 | "time" 8 | 9 | "github.com/vertiond/verthash-one-click-miner/logging" 10 | ) 11 | 12 | // Compile time assertion on interface 13 | var _ MinerImpl = &CryptoDredgeMinerImpl{} 14 | 15 | type CryptoDredgeMinerImpl struct { 16 | binaryRunner *BinaryRunner 17 | hashRates map[int64]uint64 18 | hashRatesLock sync.Mutex 19 | gpuCount int8 20 | } 21 | 22 | func NewCryptoDredgeMinerImpl(br *BinaryRunner) MinerImpl { 23 | return &CryptoDredgeMinerImpl{binaryRunner: br, hashRates: map[int64]uint64{}, hashRatesLock: sync.Mutex{}} 24 | } 25 | 26 | func (l *CryptoDredgeMinerImpl) Configure(args BinaryArguments) error { 27 | return nil 28 | } 29 | 30 | func (l *CryptoDredgeMinerImpl) ParseOutput(line string) { 31 | if l.binaryRunner.Debug { 32 | logging.Debugf("[cryptodredge] %s\n", line) 33 | } 34 | line = strings.TrimSpace(line) 35 | 36 | if strings.Contains(line, "INFO - GPU") && strings.Contains(line, "MB") { 37 | startCountIdx := strings.Index(line, "INFO - GPU") + 11 38 | gpuCountString := line[startCountIdx : startCountIdx+1] 39 | gpuCount64, _ := strconv.ParseInt(gpuCountString, 10, 8) 40 | l.gpuCount = int8(gpuCount64) + 1 41 | logging.Debugf("Set GPU Count to %d", l.gpuCount) 42 | } 43 | if strings.Contains(line, "INFO - GPU") && strings.Contains(line, "H/s") { 44 | startDeviceIdx := strings.Index(line, "INFO - GPU") 45 | endDeviceIdx := strings.Index(line[startDeviceIdx+9:], " ") 46 | deviceIdxString := line[startDeviceIdx+11 : startDeviceIdx+9+endDeviceIdx] 47 | deviceIdx, err := strconv.ParseInt(deviceIdxString, 10, 64) 48 | if err != nil { 49 | return 50 | } 51 | 52 | endMHs := strings.Index(line, "H/s") 53 | if endMHs > -1 { 54 | hashRateUnit := strings.ToUpper(line[endMHs-1 : endMHs]) 55 | line = line[:endMHs-1] 56 | line = line[strings.LastIndex(line, " ")+1:] 57 | line = strings.ReplaceAll(line, ",", ".") 58 | f, err := strconv.ParseFloat(line, 64) 59 | if err != nil { 60 | logging.Errorf("Error parsing hashrate: %s\n", err.Error()) 61 | } 62 | if hashRateUnit == "K" { 63 | f = f * 1000 64 | } else if hashRateUnit == "M" { 65 | f = f * 1000 * 1000 66 | } else if hashRateUnit == "G" { 67 | f = f * 1000 * 1000 * 1000 68 | } 69 | 70 | l.hashRatesLock.Lock() 71 | l.hashRates[deviceIdx] = uint64(f) 72 | l.hashRatesLock.Unlock() 73 | } 74 | } 75 | } 76 | 77 | func (l *CryptoDredgeMinerImpl) HashRate() uint64 { 78 | totalHash := uint64(0) 79 | l.hashRatesLock.Lock() 80 | for _, h := range l.hashRates { 81 | totalHash += h 82 | } 83 | l.hashRatesLock.Unlock() 84 | return totalHash 85 | } 86 | 87 | func (l *CryptoDredgeMinerImpl) ConstructCommandlineArgs(args BinaryArguments) []string { 88 | return []string{"--intensity", "5", "--no-color", "-a", "lyra2v3", "-o", args.StratumUrl, "-u", args.StratumUsername, "-p", args.StratumPassword} 89 | } 90 | 91 | func (l *CryptoDredgeMinerImpl) AvailableGPUs() int8 { 92 | err := l.binaryRunner.launch([]string{}, false) 93 | if err != nil { 94 | return 0 95 | } 96 | time.Sleep(time.Second) 97 | err = l.binaryRunner.Stop() 98 | if err != nil { 99 | return 0 100 | } 101 | // Output is caught by ParseOuput function above and this will set the gpuCount accordingly 102 | return l.gpuCount 103 | } 104 | -------------------------------------------------------------------------------- /frontend/src/i18n/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "generic" : { 3 | "retry" : "再試行をする", 4 | "back_to_wallet" : "ウォレットに戻る", 5 | "close" : "閉じる" 6 | }, 7 | "welcome" : { 8 | "alreadyrunning" : "ワンクリックマイナーはすでに実行中です。複数回実行することはできません。", 9 | "click_button_to_start" : "下のボタンをクリックして、マイニングを再開してください。", 10 | "startmining" : "マイニングを始める!", 11 | "makeapassword" : "入力するパスワードを無くさないでください。", 12 | "password" : "パスワード", 13 | "confirmpassword" : "パスワード(確認用)", 14 | "password_cannot_be_empty" : "パスワードは空にできません", 15 | "password_mismatch" : "新パスワードと再入力パスワードが一致しません", 16 | "error_initializing" : "ウォレットの初期化中に問題が発生しました" 17 | }, 18 | "checks" : { 19 | "prerequisite" : "前提条件がインストールされています。許可を求めてポップアップが表示されるかもしれません (またはタスクバーで点滅している可能性があります)", 20 | "checks_failed" : "チェック失敗", 21 | "checking_mining_software" : "マイニングソフトウェアの確認中...", 22 | "rapidfail" : "迅速な障害発生の確認中", 23 | "compatibility" : "GPUの互換性を確認中...", 24 | "installing_miners" : "マイニングソフトウェアのインストール中..." 25 | }, 26 | "mining" : { 27 | "spendable_balance" : "消費可能残高", 28 | "still_maturing" : "まだ成熟している", 29 | "pending_pool_payout" : "保留中のプールの支払い", 30 | "waiting_for_miners" : "マイナーが始まるのを待っている", 31 | "expected_earnings_24h" : "予想収益 (24時間)", 32 | "estimating" : "見積もり", 33 | "stop_mining" : "マイニングをやめる" 34 | }, 35 | "sending" : { 36 | "send_all_to" : "にあなたの採掘したコインをすべて送る", 37 | "youre_sending_x_to" : "に{receivedBalance}を送っています", 38 | "youre_sending_x_in_y_txs_to" : "{receivedTxCount}トランザクションで{receivedBalance}をに送信しています", 39 | "receiver_address" : "受信者アドレス", 40 | "wallet_password" : "ウォレットパスワード", 41 | "send" : "送る", 42 | "coins_sent" : "あなたのコインが送られました!", 43 | "view_trans_plural" : "取引を見る", 44 | "view_trans_singular" : "取引を見る", 45 | "failed_to_send" : "コインを送信できませんでした", 46 | "password_required" : "ウォレットパスワードが必要です", 47 | "invalid_address" : "無効なアドレス", 48 | "script_failure" : "スクリプトの失敗", 49 | "could_not_calculate_fee" : "料金を計算できませんでした", 50 | "insufficient_funds" : "残高不足", 51 | "sign_failed" : "トランザクションに署名できません。 パスワードを確認してください", 52 | "send_failed" : "取引を送信できません。 詳細についてはdebug.logを確認してください" 53 | }, 54 | "settings" : { 55 | "enable_debug" : "デバッグを有効にする", 56 | "enable_debug_sub" : "マイナーのコンソール出力をデバッグログに含めます。ログをかなり大きくすることができます", 57 | "auto_start" : "自動スタート", 58 | "auto_start_sub" : "コンピュータにログインしたらワンクリックマイナーを起動する", 59 | "closed_source" : "クローズドソースマイナーを使用する", 60 | "closed_source_sub" : "ハッシュレートは向上していますが、開発者の費用がかかる未監査のマイナー", 61 | "closed_source_warning" : "クローズドソースマイナーを使用することを選択しました。ヴァートコインはこれらのマイナーを支持しません。内容を監査することはできず、コンピュータに害を及ぼす機能が含まれている可能性があります。", 62 | "save_n_restart" : "保存して再起動" 63 | }, 64 | "tabbar" : { 65 | "wallet" : "ウォレット", 66 | "send_coins" : "コインを送る (DOGE)", 67 | "settings" : "設定" 68 | }, 69 | "tracking": { 70 | "update_available" : "アップデート利用可能", 71 | "tracking_enabled" : "利用統計を匿名で共有しています", 72 | "disable_tracking" : "無効にする", 73 | "tracking_disabled" : "利用統計を共有していません", 74 | "enable_tracking" : "これらを有効にして利用体験の改善を行いさせるのを助けてください", 75 | "report_issue" : "問題を報告する" 76 | }, 77 | "update" : { 78 | "new_version_available" : "新バージョン利用可能", 79 | "download" : "ダウンロード" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /frontend/src/i18n/README.md: -------------------------------------------------------------------------------- 1 | # Translation workflow 2 | 3 | If you want to add a new translation to the Vertcoin OCM you can follow two tracks: 4 | 5 | ## Create a pull-request (preferable) 6 | 7 | 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/) 8 | 9 | ### Step 1: Create a copy of the english base file 10 | 11 | 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`). 12 | 13 | ### Step 2: Translate! 14 | 15 | Translate all the strings in the javascript file. Only translate the values not the identifiers, so: 16 | 17 | ```json 18 | { 19 | "generic" : { 20 | "retry" : "Retry", 21 | "back_to_wallet" : "Back to wallet" 22 | }, 23 | ... 24 | } 25 | ``` 26 | 27 | Would become 28 | 29 | ```json 30 | { 31 | "generic" : { 32 | "retry" : "Opnieuw proberen", 33 | "back_to_wallet" : "Terug naar portemonnee" 34 | }, 35 | ... 36 | } 37 | ``` 38 | **NOTE: Special characters** 39 | 40 | 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) 41 | 42 | **NOTE: Character set** 43 | 44 | Please ensure the JSON file is saved using an UTF-8 character set. 45 | 46 | ### Step 3: Add language to frontend 47 | 48 | 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: 49 | 50 | ```javascript 51 | // Import all locales 52 | import locale_de from "./i18n/de.json"; // <-- this line is added 53 | import locale_en from "./i18n/en.json"; 54 | import locale_nl from "./i18n/nl.json"; 55 | ``` 56 | 57 | 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: 58 | 59 | ```javascript 60 | const i18n = new VueI18n({ 61 | locale: result, // set locale 62 | messages : { 63 | de: locale_de, // <-- this line is added 64 | en: locale_en, 65 | nl: locale_nl, 66 | }, 67 | }); 68 | ``` 69 | 70 | ### Step 4: Add language to the backend 71 | 72 | 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: 73 | 74 | ```golang 75 | var availableLanguages = []string{ 76 | "de", // <-- this line is added. Notice the comma on the end - it belongs there! 77 | "en", 78 | "nl", 79 | } 80 | ``` 81 | 82 | ### Step 5: Commit & Create PR 83 | 84 | 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! 85 | 86 | ## Alternative option (only if you fail at the above) 87 | 88 | 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! -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | "runtime/debug" 9 | 10 | _ "embed" 11 | 12 | "github.com/marcsauter/single" 13 | "github.com/vertiond/verthash-one-click-miner/backend" 14 | "github.com/vertiond/verthash-one-click-miner/logging" 15 | "github.com/vertiond/verthash-one-click-miner/networks" 16 | //"github.com/vertiond/verthash-one-click-miner/ping" 17 | "github.com/vertiond/verthash-one-click-miner/tracking" 18 | "github.com/vertiond/verthash-one-click-miner/util" 19 | "github.com/wailsapp/wails" 20 | ) 21 | 22 | //go:embed frontend/dist/app.js 23 | var js string 24 | 25 | //go:embed frontend/dist/app.css 26 | var css string 27 | 28 | func main() { 29 | defer func() { 30 | if err := recover(); err != nil { 31 | // Reopen log file, since it's closed now! 32 | logging.SetLogLevel(int(logging.LogLevelDebug)) 33 | logFilePath := filepath.Join(util.DataDirectory(), "debug.log") 34 | logFile, _ := os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) 35 | logging.SetLogFile(logFile) 36 | defer logFile.Close() 37 | logging.Errorf("%v\n%s\n", err, string(debug.Stack())) 38 | 39 | tracking.Track(tracking.TrackingRequest{ 40 | Category: "Lifecycle", 41 | Action: "Crash", 42 | Name: fmt.Sprintf("%v", err), 43 | }) 44 | 45 | } 46 | }() 47 | 48 | tracking.StartTracker() 49 | 50 | tracking.Track(tracking.TrackingRequest{ 51 | Category: "Lifecycle", 52 | Action: "Startup", 53 | Name: fmt.Sprintf("OCM/%s", tracking.GetVersion()), 54 | }) 55 | 56 | logging.SetLogLevel(int(logging.LogLevelDebug)) 57 | if _, err := os.Stat(util.DataDirectory()); os.IsNotExist(err) { 58 | logging.Infof("Creating data directory") 59 | err = os.MkdirAll(util.DataDirectory(), 0700) 60 | if err != nil && !os.IsExist(err) { 61 | logging.Errorf("Error creating data directory, cannot continue") 62 | os.Exit(1) 63 | } 64 | } 65 | 66 | logFilePath := filepath.Join(util.DataDirectory(), "debug.log") 67 | logFile, _ := os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) 68 | logging.SetLogFile(logFile) 69 | defer logFile.Close() 70 | 71 | log.Printf("OCM v%s Started up\n", tracking.GetVersion()) 72 | 73 | app := wails.CreateApp(&wails.AppConfig{ 74 | Width: 800, 75 | Height: 430, 76 | Title: "Verthash One Click Miner", 77 | JS: js, 78 | CSS: css, 79 | Colour: "#131313", 80 | }) 81 | 82 | alreadyRunning := false 83 | s := single.New("verthash-ocm") 84 | if err := s.CheckLock(); err != nil && err == single.ErrAlreadyRunning { 85 | alreadyRunning = true 86 | } else if err == nil { 87 | defer func() { 88 | err := s.TryUnlock() 89 | if err != nil { 90 | logging.Errorf("Error unlocking OCM: %v", err) 91 | } 92 | }() 93 | } 94 | 95 | backend, err := backend.NewBackend(alreadyRunning) 96 | if err != nil { 97 | logging.Errorf("Error creating Backend: %s", err.Error()) 98 | panic(err) 99 | } 100 | networks.SetNetwork(backend.GetTestnet()) 101 | go backend.BackendServerSelector() 102 | //ping.GetSelectedNode(backend.GetTestnet()) 103 | 104 | backend.ResetWalletAddress() 105 | backend.ResetPool() 106 | backend.ResetCustomAddress() 107 | backend.ResetPayout() 108 | app.Bind(backend) 109 | err = app.Run() 110 | if err != nil { 111 | logging.Errorf("Error running app: %v", err) 112 | } 113 | backend.StopMining() 114 | 115 | tracking.Track(tracking.TrackingRequest{ 116 | Category: "Lifecycle", 117 | Action: "Shutdown", 118 | }) 119 | 120 | tracking.Stop() 121 | } 122 | -------------------------------------------------------------------------------- /backend/wallet.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/vertiond/verthash-one-click-miner/keyfile" 7 | "github.com/vertiond/verthash-one-click-miner/logging" 8 | "github.com/vertiond/verthash-one-click-miner/networks" 9 | "github.com/vertiond/verthash-one-click-miner/tracking" 10 | "github.com/vertiond/verthash-one-click-miner/util" 11 | "github.com/vertiond/verthash-one-click-miner/wallet_doge" 12 | ) 13 | 14 | func (m *Backend) WalletInitialized() int { 15 | logging.Infof("Checking wallet..") 16 | checkWallet := 0 17 | if keyfile.KeyFileValid() { 18 | checkWallet = 1 19 | } 20 | script, err := keyfile.GetScript() 21 | if err != nil { 22 | logging.Errorf("Error initializing wallet: %s", err.Error()) 23 | } 24 | wal, err := wallet.NewWallet(keyfile.GetAddress(), script) 25 | if err != nil { 26 | logging.Errorf("Error initializing wallet: %s", err.Error()) 27 | } 28 | m.wal = wal 29 | logging.Infof("Wallet initialized: %d", checkWallet) 30 | return checkWallet 31 | } 32 | 33 | func (m *Backend) SendSweep(password string) []string { 34 | tracking.Track(tracking.TrackingRequest{ 35 | Category: "Sweep", 36 | Action: "Send", 37 | }) 38 | 39 | txids := make([]string, 0) 40 | 41 | if len(m.pendingSweep) == 0 { 42 | // Somehow user managed to press send without properly 43 | // preparing the sweep first 44 | return []string{"send_failed"} 45 | } 46 | 47 | for _, s := range m.pendingSweep { 48 | err := m.wal.SignMyInputs(s, password) 49 | if err != nil { 50 | logging.Errorf("Error signing transaction: %s", err.Error()) 51 | return []string{"sign_failed"} 52 | } 53 | 54 | txHash, err := m.wal.Send(s) 55 | if err != nil { 56 | logging.Errorf("Error sending transaction: %s", err.Error()) 57 | return []string{"send_failed"} 58 | } 59 | txids = append(txids, txHash) 60 | } 61 | 62 | m.pendingSweep = nil 63 | 64 | logging.Debugf("Transaction(s) sent! TXIDs: %v\n", txids) 65 | m.refreshBalanceChan <- true 66 | return txids 67 | 68 | } 69 | 70 | func (m *Backend) ShowTx(txid string) { 71 | util.OpenBrowser(fmt.Sprintf("%stx/DOGE/%s", networks.Active.InsightURL, txid)) 72 | } 73 | 74 | type PrepareResult struct { 75 | FormattedAmount string 76 | NumberOfTransactions int 77 | } 78 | 79 | func (m *Backend) PrepareSweep(addr string) string { 80 | tracking.Track(tracking.TrackingRequest{ 81 | Category: "Sweep", 82 | Action: "Prepare", 83 | }) 84 | 85 | logging.Debugf("Preparing sweep") 86 | 87 | txs, err := m.wal.PrepareSweep(addr) 88 | if err != nil { 89 | logging.Errorf("Error preparing sweep: %v", err) 90 | return err.Error() 91 | } 92 | 93 | m.pendingSweep = txs 94 | val := float64(0) 95 | for _, tx := range txs { 96 | val += (float64(tx.TxOut[0].Value) / float64(100000000)) 97 | } 98 | 99 | result := PrepareResult{fmt.Sprintf("%0.8f DOGE", val), len(txs)} 100 | logging.Debugf("Prepared sweep: %v", result) 101 | 102 | m.runtime.Events.Emit("createTransactionResult", result) 103 | return "" 104 | } 105 | 106 | func (m *Backend) Address() string { 107 | return keyfile.GetAddress() 108 | } 109 | 110 | func (m *Backend) InitWallet(password string) bool { 111 | tracking.Track(tracking.TrackingRequest{ 112 | Category: "Wallet", 113 | Action: "Initialize", 114 | }) 115 | 116 | err := keyfile.CreateKeyFile(password) 117 | if err == nil { 118 | m.WalletInitialized() 119 | m.ResetPool() 120 | m.ResetPayout() 121 | m.ResetWalletAddress() 122 | return true 123 | } 124 | logging.Errorf("Error: %s", err.Error()) 125 | return false 126 | } 127 | -------------------------------------------------------------------------------- /tracking/matomo.go: -------------------------------------------------------------------------------- 1 | package tracking 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/hex" 6 | "fmt" 7 | "log" 8 | "net/http" 9 | "os" 10 | "path/filepath" 11 | "runtime" 12 | "strings" 13 | "sync" 14 | "time" 15 | 16 | "github.com/vertiond/verthash-one-click-miner/logging" 17 | "github.com/vertiond/verthash-one-click-miner/util" 18 | ) 19 | 20 | type TrackingRequest struct { 21 | Category string 22 | Action string 23 | Name string 24 | } 25 | 26 | var trackChan chan TrackingRequest 27 | var trackingEnabled bool 28 | var waitGroup sync.WaitGroup 29 | 30 | func Enable() { 31 | trackingEnabled = true 32 | saveState() 33 | } 34 | 35 | func IsEnabled() bool { 36 | return trackingEnabled 37 | } 38 | 39 | func Disable() { 40 | trackingEnabled = false 41 | saveState() 42 | } 43 | 44 | func loadState() { 45 | dat, err := os.ReadFile(filepath.Join(util.DataDirectory(), "tracking")) 46 | if err != nil { 47 | // // Default enable tracking 48 | // Enable() 49 | // Default disable tracking 50 | Disable() 51 | return 52 | } 53 | trackingEnabled = (string(dat) == "1") 54 | if trackingEnabled { 55 | Disable() 56 | } 57 | } 58 | 59 | func saveState() { 60 | value := "1" 61 | if !trackingEnabled { 62 | value = "0" 63 | } 64 | err := os.WriteFile(filepath.Join(util.DataDirectory(), "tracking"), []byte(value), 0644) 65 | if err != nil { 66 | logging.Errorf("Error writing tracking state: %v", err) 67 | } 68 | } 69 | 70 | func StartTracker() { 71 | matomoClient := &http.Client{Timeout: 2 * time.Second} 72 | trackChan = make(chan TrackingRequest, 100) 73 | waitGroup = sync.WaitGroup{} 74 | loadState() 75 | new := true 76 | waitGroup.Add(1) 77 | go func() { 78 | for t := range trackChan { 79 | if !trackingEnabled { 80 | continue 81 | } 82 | req, err := http.NewRequest("GET", "https://matomo.gertjaap.org/matomo.php", nil) 83 | if err != nil { 84 | log.Print(err) 85 | os.Exit(1) 86 | } 87 | 88 | q := req.URL.Query() 89 | q.Add("idsite", "2") 90 | q.Add("rec", "1") 91 | if new { 92 | new = false 93 | q.Add("new_visit", "1") 94 | } 95 | q.Add("action_name", fmt.Sprintf("%s/%s", t.Category, t.Action)) 96 | q.Add("e_c", t.Category) 97 | q.Add("e_a", t.Action) 98 | q.Add("e_n", t.Name) 99 | q.Add("ua", userAgent()) 100 | q.Add("_id", visitorId()) 101 | req.URL.RawQuery = q.Encode() 102 | 103 | r, err := matomoClient.Do(req) 104 | if err != nil { 105 | logging.Warnf("Error sending tracking data: %v", err) 106 | continue 107 | } 108 | if r.Body != nil { 109 | r.Body.Close() 110 | } 111 | } 112 | waitGroup.Done() 113 | }() 114 | } 115 | 116 | func Stop() { 117 | close(trackChan) 118 | waitGroup.Wait() 119 | } 120 | 121 | func Track(req TrackingRequest) { 122 | trackChan <- req 123 | } 124 | 125 | var vId string 126 | 127 | func visitorId() string { 128 | if vId == "" { 129 | dat, err := os.ReadFile(filepath.Join(util.DataDirectory(), "unique_id")) 130 | if err != nil { 131 | dat = make([]byte, 8) 132 | _, err := rand.Read(dat) 133 | if err != nil { 134 | logging.Errorf("Error reading random ID: %v", err) 135 | } 136 | err = os.WriteFile(filepath.Join(util.DataDirectory(), "unique_id"), dat, 0644) 137 | if err != nil { 138 | logging.Errorf("Error writing random ID: %v", err) 139 | } 140 | 141 | } 142 | vId = strings.ToUpper(hex.EncodeToString(dat)) 143 | } 144 | return vId 145 | } 146 | 147 | var ua string 148 | 149 | func userAgent() string { 150 | if ua == "" { 151 | ua = fmt.Sprintf("OCM/%s %s/%s", GetVersion(), runtime.GOOS, runtime.GOARCH) 152 | } 153 | return ua 154 | } 155 | 156 | func GetVersion() string { 157 | return version 158 | } 159 | -------------------------------------------------------------------------------- /frontend/src/App.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 117 | -------------------------------------------------------------------------------- /util/gpus.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "regexp" 7 | "runtime" 8 | "strings" 9 | 10 | "github.com/vertiond/verthash-one-click-miner/logging" 11 | ) 12 | 13 | type GPUType int 14 | 15 | const ( 16 | GPUTypeOther GPUType = 0 17 | GPUTypeAMD GPUType = 1 18 | GPUTypeNVidia GPUType = 2 19 | GPUTypeIntel GPUType = 3 20 | ) 21 | 22 | type GPU struct { 23 | OSName string 24 | Type GPUType 25 | } 26 | 27 | type KnownGPU struct { 28 | RegExPattern string 29 | Type GPUType 30 | RegExp *regexp.Regexp 31 | } 32 | 33 | var gpusCache []GPU 34 | var gpusCached = false 35 | 36 | var knownGPUs = []KnownGPU{ 37 | /*KnownGPU{"Radeon( \\(TM\\))?( RX)? (Vega|[4-5][6-9]0)", GPUTypeAMD, nil}, 38 | KnownGPU{"AMD Radeon\\(TM\\) R[79] Graphics", GPUTypeAMD, nil}, 39 | KnownGPU{"AMD Radeon VII", GPUTypeAMD, nil}, 40 | KnownGPU{"NVIDIA P[0-9]{3}-[0-9]{3}", GPUTypeNVidia, nil}, 41 | KnownGPU{"NVIDIA GeForce (RTX )?(GTX )?(10|16|20|[7-9])[0-9]{2}( ti)?(MX)?", GPUTypeNVidia, nil}, 42 | KnownGPU{"Advanced Micro Devices, Inc. \\[AMD/ATI\\] .*", GPUTypeAMD, nil},*/ 43 | KnownGPU{".*NVIDIA.*", GPUTypeNVidia, nil}, 44 | KnownGPU{".*AMD.*", GPUTypeAMD, nil}, 45 | KnownGPU{".*Intel.*", GPUTypeIntel, nil}, 46 | KnownGPU{".*Radeon.*", GPUTypeAMD, nil}, 47 | } 48 | 49 | func init() { 50 | if os.Getenv("OCM_VIRTUALBOX") == "1" { 51 | knownGPUs = append(knownGPUs, KnownGPU{".*VirtualBox.*", GPUTypeIntel, nil}) 52 | } 53 | 54 | for i := range knownGPUs { 55 | knownGPUs[i].RegExp, _ = regexp.Compile(knownGPUs[i].RegExPattern) 56 | } 57 | } 58 | 59 | func GetGPUsFromStrings(names []string) []GPU { 60 | gpus := []GPU{} 61 | for _, n := range names { 62 | found := false 63 | for _, k := range knownGPUs { 64 | if k.RegExp.Match([]byte(n)) { 65 | logging.Debugf("GPU [%s] matched regex [%s]\n", n, k.RegExp) 66 | gpus = append(gpus, GPU{n, k.Type}) 67 | found = true 68 | break 69 | } 70 | } 71 | if !found { 72 | logging.Debugf("Unmatched GPU: [%s]\n", n) 73 | gpus = append(gpus, GPU{n, GPUTypeOther}) 74 | } 75 | } 76 | return gpus 77 | } 78 | 79 | func GetGPUs() []GPU { 80 | if !gpusCached { 81 | gpus := []string{} 82 | if runtime.GOOS == "windows" { 83 | info := exec.Command("cmd", "/C", "wmic path win32_VideoController get name") 84 | PrepareBackgroundCommand(info) 85 | history, _ := info.Output() 86 | possibleGpus := strings.Split(string(history), "\n") 87 | for _, g := range possibleGpus { 88 | g = strings.Trim(g, "\r ") 89 | if g != "" && g != "Name" { 90 | gpus = append(gpus, g) 91 | } 92 | } 93 | } else if runtime.GOOS == "linux" { 94 | Info := exec.Command("lspci") 95 | History, _ := Info.Output() 96 | lines := strings.Split(string(History), "\n") 97 | for _, l := range lines { 98 | vgaIdx := strings.Index(l, "VGA compatible: ") 99 | if vgaIdx > -1 { 100 | gpus = append(gpus, l[vgaIdx+16:]) 101 | } 102 | 103 | vgaIdx = strings.Index(l, "VGA compatible controller: ") 104 | if vgaIdx > -1 { 105 | gpus = append(gpus, l[vgaIdx+27:]) 106 | } 107 | 108 | vgaIdx = strings.Index(l, "3D controller: ") 109 | if vgaIdx > -1 { 110 | gpus = append(gpus, l[vgaIdx+15:]) 111 | } 112 | } 113 | } else if runtime.GOOS == "darwin" { 114 | Info := exec.Command("system_profiler", "SPDisplaysDataType") 115 | History, _ := Info.Output() 116 | lines := strings.Split(string(History), "\n") 117 | for _, l := range lines { 118 | csIdx := strings.Index(l, "Chipset Model: ") 119 | if csIdx > -1 { 120 | gpus = append(gpus, l[csIdx+15:]) 121 | } 122 | } 123 | } 124 | gpusCache = GetGPUsFromStrings(gpus) 125 | gpusCached = true 126 | } 127 | return gpusCache 128 | } 129 | -------------------------------------------------------------------------------- /logging/log.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | // Log Levels: 4 | // 3: DebugLevel prints Panics, Fatals, Errors, Warnings, Infos and Debugs 5 | // 2: InfoLevel prints Panics, Fatals, Errors, Warnings and Info 6 | // 1: WarnLevel prints Panics, Fatals, Errors and Warnings 7 | // 0: ErrorLevel prints Panics, Fatals and Errors 8 | // Default is level 0 9 | // Code for tagging logs: 10 | // Debug -> Useful debugging information 11 | // Info -> Something noteworthy happened 12 | // Warn -> You should probably take a look at this 13 | // Error -> Something failed but I'm not quitting 14 | // Fatal -> Bye 15 | 16 | import ( 17 | "fmt" 18 | "io" 19 | "log" 20 | "os" 21 | ) 22 | 23 | type LogLevel int 24 | 25 | const ( 26 | LogLevelError LogLevel = 0 27 | LogLevelWarning LogLevel = 1 28 | LogLevelInfo LogLevel = 2 29 | LogLevelDebug LogLevel = 3 30 | ) 31 | 32 | var logLevel = LogLevelError // the default 33 | 34 | func SetLogLevel(newLevel int) { 35 | logLevel = LogLevel(newLevel) 36 | } 37 | 38 | func SetLogFile(logFile io.Writer) { 39 | log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds) 40 | _, err := os.Stdout.Write([]byte("\n")) 41 | if err == nil { 42 | log.SetOutput(io.MultiWriter(os.Stdout, logFile)) 43 | } else { 44 | log.SetOutput(logFile) 45 | } 46 | } 47 | 48 | func getPrefix(level string) string { 49 | return fmt.Sprintf("[%s]", level) 50 | } 51 | 52 | func Fatalln(args ...interface{}) { 53 | log.Fatalln(args...) 54 | } 55 | 56 | func Fatalf(format string, args ...interface{}) { 57 | log.Fatalf(format, args...) 58 | } 59 | 60 | func Fatal(args ...interface{}) { 61 | log.Fatal(args...) 62 | } 63 | 64 | func Debugf(format string, args ...interface{}) { 65 | if logLevel >= LogLevelDebug { 66 | log.Printf(fmt.Sprintf("%s %s", getPrefix("DEBUG"), format), args...) 67 | } 68 | } 69 | 70 | func Infof(format string, args ...interface{}) { 71 | if logLevel >= LogLevelInfo { 72 | log.Printf(fmt.Sprintf("%s %s", getPrefix("INFO"), format), args...) 73 | } 74 | } 75 | 76 | func Warnf(format string, args ...interface{}) { 77 | if logLevel >= LogLevelWarning { 78 | log.Printf(fmt.Sprintf("%s %s", getPrefix("WARN"), format), args...) 79 | } 80 | } 81 | 82 | func Errorf(format string, args ...interface{}) { 83 | if logLevel >= LogLevelError { 84 | log.Printf(fmt.Sprintf("%s %s", getPrefix("ERROR"), format), args...) 85 | } 86 | } 87 | 88 | func Debugln(args ...interface{}) { 89 | if logLevel >= LogLevelDebug { 90 | args = append([]interface{}{getPrefix("DEBUG")}, args...) 91 | log.Println(args...) 92 | } 93 | } 94 | 95 | func Infoln(args ...interface{}) { 96 | if logLevel >= LogLevelInfo { 97 | args = append([]interface{}{getPrefix("INFO")}, args...) 98 | log.Println(args...) 99 | } 100 | } 101 | 102 | func Warnln(args ...interface{}) { 103 | if logLevel >= LogLevelWarning { 104 | args = append([]interface{}{getPrefix("WARN")}, args...) 105 | log.Println(args...) 106 | } 107 | } 108 | 109 | func Errorln(args ...interface{}) { 110 | if logLevel >= LogLevelError { 111 | args = append([]interface{}{getPrefix("ERROR")}, args...) 112 | log.Println(args...) 113 | } 114 | } 115 | 116 | func Debug(args ...interface{}) { 117 | if logLevel >= LogLevelDebug { 118 | args = append([]interface{}{getPrefix("DEBUG")}, args...) 119 | log.Print(args...) 120 | } 121 | } 122 | 123 | func Info(args ...interface{}) { 124 | if logLevel >= LogLevelInfo { 125 | args = append([]interface{}{getPrefix("INFO")}, args...) 126 | log.Print(args...) 127 | } 128 | } 129 | 130 | func Warn(args ...interface{}) { 131 | if logLevel >= LogLevelWarning { 132 | args = append([]interface{}{getPrefix("WARN")}, args...) 133 | log.Print(args...) 134 | } 135 | } 136 | 137 | func Error(args ...interface{}) { 138 | if logLevel >= LogLevelError { 139 | args = append([]interface{}{getPrefix("ERROR")}, args...) 140 | log.Print(args...) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /wallet/sigops.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "github.com/btcsuite/btcd/txscript" 5 | "github.com/btcsuite/btcutil" 6 | ) 7 | 8 | var WitnessScaleFactor = 4 9 | 10 | // CountSigOps returns the number of signature operations for all transaction 11 | // input and output scripts in the provided transaction. This uses the 12 | // quicker, but imprecise, signature operation counting mechanism from 13 | // txscript. 14 | func CountSigOps(tx *btcutil.Tx) int { 15 | msgTx := tx.MsgTx() 16 | 17 | // Accumulate the number of signature operations in all transaction 18 | // inputs. 19 | totalSigOps := 0 20 | for _, txIn := range msgTx.TxIn { 21 | numSigOps := txscript.GetSigOpCount(txIn.SignatureScript) 22 | totalSigOps += numSigOps 23 | } 24 | 25 | // Accumulate the number of signature operations in all transaction 26 | // outputs. 27 | for _, txOut := range msgTx.TxOut { 28 | numSigOps := txscript.GetSigOpCount(txOut.PkScript) 29 | totalSigOps += numSigOps 30 | } 31 | 32 | return totalSigOps 33 | } 34 | 35 | func (w *Wallet) GetSigOpCost(tx *btcutil.Tx, pkScript []byte, isCoinBaseTx bool, bip16, segWit bool) (int, error) { 36 | numSigOps := CountSigOps(tx) * WitnessScaleFactor 37 | if bip16 { 38 | numP2SHSigOps, err := w.CountP2SHSigOps(tx, isCoinBaseTx) 39 | if err != nil { 40 | return 0, nil 41 | } 42 | numSigOps += (numP2SHSigOps * WitnessScaleFactor) 43 | } 44 | 45 | if segWit && !isCoinBaseTx { 46 | msgTx := tx.MsgTx() 47 | for _, txIn := range msgTx.TxIn { 48 | witness := txIn.Witness 49 | sigScript := txIn.SignatureScript 50 | numSigOps += txscript.GetWitnessSigOpCount(sigScript, pkScript, witness) 51 | } 52 | 53 | } 54 | 55 | return numSigOps, nil 56 | } 57 | 58 | // CountP2SHSigOps returns the number of signature operations for all input 59 | // transactions which are of the pay-to-script-hash type. This uses the 60 | // precise, signature operation counting mechanism from the script engine which 61 | // requires access to the input transaction scripts. 62 | func (w *Wallet) CountP2SHSigOps(tx *btcutil.Tx, isCoinBaseTx bool) (int, error) { 63 | // We never spend P2SH in OCM, so this can be disabled 64 | return 0, nil 65 | /* 66 | // Coinbase transactions have no interesting inputs. 67 | if isCoinBaseTx { 68 | return 0, nil 69 | } 70 | 71 | // Accumulate the number of signature operations in all transaction 72 | // inputs. 73 | msgTx := tx.MsgTx() 74 | totalSigOps := 0 75 | for txInIndex, txIn := range msgTx.TxIn { 76 | // Ensure the referenced input transaction is available. 77 | utxo := w.GetUtxo(txIn.PreviousOutPoint.Hash.String(), uint(txIn.PreviousOutPoint.Index)) 78 | if utxo.TxID == "" { 79 | str := fmt.Sprintf("output %v referenced from "+ 80 | "transaction %s:%d either does not exist or "+ 81 | "has already been spent", txIn.PreviousOutPoint, 82 | tx.Hash(), txInIndex) 83 | return 0, fmt.Errorf(str) 84 | } 85 | 86 | // We're only interested in pay-to-script-hash types, so skip 87 | // this input if it's not one. 88 | pkScript, _ := hex.DecodeString(utxo.ScriptPubKey) 89 | if !txscript.IsPayToScriptHash(pkScript) { 90 | continue 91 | } 92 | 93 | // Count the precise number of signature operations in the 94 | // referenced public key script. 95 | sigScript := txIn.SignatureScript 96 | numSigOps := txscript.GetPreciseSigOpCount(sigScript, pkScript, 97 | true) 98 | 99 | // We could potentially overflow the accumulator so check for 100 | // overflow. 101 | lastSigOps := totalSigOps 102 | totalSigOps += numSigOps 103 | if totalSigOps < lastSigOps { 104 | str := fmt.Sprintf("the public key script from output "+ 105 | "%v contains too many signature operations - "+ 106 | "overflow", txIn.PreviousOutPoint) 107 | return 0, fmt.Errorf(str) 108 | } 109 | } 110 | 111 | return totalSigOps, nil*/ 112 | } 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Verthash One-Click Miner 2 | 3 | This program will create a Dogecoin wallet that only you have access to. Encrypt your wallet with a password that you will not forget. It will then create the Verthash datafile and immediately begin mining Vertcoin. You will receive payouts in Dogecoin to your built-in Dogecoin wallet from the pool selected in Settings. Additional payout currencies via Zergpool and zpool are available in Settings. 4 | 5 | ### What are the fees? 6 | 7 | - This program and the [miner](https://github.com/CryptoGraphics/VerthashMiner) it runs are completely free and have no fee 8 | - [Zergpool.com](https://zergpool.com/) and [zpool.ca](https://www.zpool.ca/) charge a 0.5% fee while mining 9 | - [HashCryptos.com](https://www.hashcryptos.com/) charges no fee (DOGE only) 10 | - Standard minimum Dogecoin network fee for withdrawal from built-in wallet (usually < 0.001 DOGE) 11 | 12 | ### When do I get paid? 13 | 14 | - **Zergpool.com and zpool.ca** 15 | - Please check 'View Payout Information' for payout thresholds 16 | - **HashCryptos.com** 17 | - Every 24 hours with the ability to change payout frequency to every 12 hours (DOGE only) 18 | 19 | ### What coins does this support? 20 | 21 | - Dogecoin 22 | - Vertcoin 23 | - Binance Coin 24 | - Bitcoin 25 | - Bitcoin Cash 26 | - Cake 27 | - Dash 28 | - Digibyte 29 | - Ethereum 30 | - Ethereum Classic 31 | - Firo (Zcoin) 32 | - Flux (Zelcash) 33 | - Groestlcoin 34 | - Horizen (Zencash) 35 | - Komodo 36 | - LBRY Credits 37 | - Litecoin 38 | - Monero 39 | - Peercoin 40 | - Polygon(ERC20) 41 | - Raptoreum 42 | - Ravencoin 43 | - Shiba Inu(ERC20) 44 | - Tether(ERC20, TRC20, BEP20) 45 | - Tron 46 | - Verge 47 | - Zcash 48 | - Additional coins may be added via PR following this [guide](https://github.com/vertiond/verthash-one-click-miner/blob/master/ADD-A-COIN.md) 49 | 50 | The Verthash One-Click Miner mines [Vertcoin](https://vertcoin.org) and is functionally the same as [upstream](https://github.com/vertcoin-project/one-click-miner-vnext) utilizing a new data directory, `verthash-ocm`. This is essentially a fancy wrapper for [VerthashMiner](https://github.com/CryptoGraphics/VerthashMiner) which is also open source. 51 | 52 | This software is available for Windows and Linux. 53 | 54 | ## FAQ 55 | 56 | ### Which GPUs are supported? 57 | 58 | Please refer to this list of [supported hardware.](https://github.com/CryptoGraphics/VerthashMiner#supported-hardware) 59 | 60 | ### I have an error message that reads 'Failure to configure' or 'Checks failed' 61 | 62 | You may need to add an exclusion to your antivirus / Windows Defender. The data directory is located at `%AppData%\verthash-ocm` on Windows or `~/.verthash-ocm` on Linux. 63 | 64 | ### My GPU is supported but an error messages reads 'no compatible GPUs' 65 | 66 | Update your GPU drivers to the latest version. 67 | 68 | ### My GPU is not being utilized 69 | 70 | The OCM is using all of your GPU resources. To verify, check CUDA usage for Nvidia or Compute 0/1 usage for AMD. 71 | 72 | ### I selected HashCryptos.com but Expected Earnings says zero 73 | 74 | Please make sure you have [activated your address.](https://www.hashcryptos.com/) It may take a few minutes to activate before you see Expected Earnings and hashrate. 75 | 76 | ## Building 77 | 78 | The GUI is based on [Wails](https://wails.app) and [Go](https://golang.org/). 79 | 80 | Install the Wails [prerequisites](https://wails.app/gettingstarted/) for your platform, and then run: 81 | 82 | ```bash 83 | go get github.com/wailsapp/wails/cmd/wails 84 | ``` 85 | 86 | Then clone this repository, and inside its main folder, execute: 87 | 88 | ```bash 89 | wails build 90 | ``` 91 | 92 | ## Donations 93 | 94 | If you want to support the further development of the upstream One Click Miner, feel free to donate Vertcoin to [Vmnbtn5nnNbs1otuYa2LGBtEyFuarFY1f8](https://insight.vertcoin.org/address/Vmnbtn5nnNbs1otuYa2LGBtEyFuarFY1f8). 95 | -------------------------------------------------------------------------------- /wallet_doge/sigops.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "github.com/btcsuite/btcd/txscript" 5 | "github.com/btcsuite/btcutil" 6 | ) 7 | 8 | var WitnessScaleFactor = 4 9 | 10 | // CountSigOps returns the number of signature operations for all transaction 11 | // input and output scripts in the provided transaction. This uses the 12 | // quicker, but imprecise, signature operation counting mechanism from 13 | // txscript. 14 | func CountSigOps(tx *btcutil.Tx) int { 15 | msgTx := tx.MsgTx() 16 | 17 | // Accumulate the number of signature operations in all transaction 18 | // inputs. 19 | totalSigOps := 0 20 | for _, txIn := range msgTx.TxIn { 21 | numSigOps := txscript.GetSigOpCount(txIn.SignatureScript) 22 | totalSigOps += numSigOps 23 | } 24 | 25 | // Accumulate the number of signature operations in all transaction 26 | // outputs. 27 | for _, txOut := range msgTx.TxOut { 28 | numSigOps := txscript.GetSigOpCount(txOut.PkScript) 29 | totalSigOps += numSigOps 30 | } 31 | 32 | return totalSigOps 33 | } 34 | 35 | func (w *Wallet) GetSigOpCost(tx *btcutil.Tx, pkScript []byte, isCoinBaseTx bool, bip16, segWit bool) (int, error) { 36 | numSigOps := CountSigOps(tx) * WitnessScaleFactor 37 | if bip16 { 38 | numP2SHSigOps, err := w.CountP2SHSigOps(tx, isCoinBaseTx) 39 | if err != nil { 40 | return 0, nil 41 | } 42 | numSigOps += (numP2SHSigOps * WitnessScaleFactor) 43 | } 44 | 45 | if segWit && !isCoinBaseTx { 46 | msgTx := tx.MsgTx() 47 | for _, txIn := range msgTx.TxIn { 48 | witness := txIn.Witness 49 | sigScript := txIn.SignatureScript 50 | numSigOps += txscript.GetWitnessSigOpCount(sigScript, pkScript, witness) 51 | } 52 | 53 | } 54 | 55 | return numSigOps, nil 56 | } 57 | 58 | // CountP2SHSigOps returns the number of signature operations for all input 59 | // transactions which are of the pay-to-script-hash type. This uses the 60 | // precise, signature operation counting mechanism from the script engine which 61 | // requires access to the input transaction scripts. 62 | func (w *Wallet) CountP2SHSigOps(tx *btcutil.Tx, isCoinBaseTx bool) (int, error) { 63 | // We never spend P2SH in OCM, so this can be disabled 64 | return 0, nil 65 | /* 66 | // Coinbase transactions have no interesting inputs. 67 | if isCoinBaseTx { 68 | return 0, nil 69 | } 70 | 71 | // Accumulate the number of signature operations in all transaction 72 | // inputs. 73 | msgTx := tx.MsgTx() 74 | totalSigOps := 0 75 | for txInIndex, txIn := range msgTx.TxIn { 76 | // Ensure the referenced input transaction is available. 77 | utxo := w.GetUtxo(txIn.PreviousOutPoint.Hash.String(), uint(txIn.PreviousOutPoint.Index)) 78 | if utxo.TxID == "" { 79 | str := fmt.Sprintf("output %v referenced from "+ 80 | "transaction %s:%d either does not exist or "+ 81 | "has already been spent", txIn.PreviousOutPoint, 82 | tx.Hash(), txInIndex) 83 | return 0, fmt.Errorf(str) 84 | } 85 | 86 | // We're only interested in pay-to-script-hash types, so skip 87 | // this input if it's not one. 88 | pkScript, _ := hex.DecodeString(utxo.ScriptPubKey) 89 | if !txscript.IsPayToScriptHash(pkScript) { 90 | continue 91 | } 92 | 93 | // Count the precise number of signature operations in the 94 | // referenced public key script. 95 | sigScript := txIn.SignatureScript 96 | numSigOps := txscript.GetPreciseSigOpCount(sigScript, pkScript, 97 | true) 98 | 99 | // We could potentially overflow the accumulator so check for 100 | // overflow. 101 | lastSigOps := totalSigOps 102 | totalSigOps += numSigOps 103 | if totalSigOps < lastSigOps { 104 | str := fmt.Sprintf("the public key script from output "+ 105 | "%v contains too many signature operations - "+ 106 | "overflow", txIn.PreviousOutPoint) 107 | return 0, fmt.Errorf(str) 108 | } 109 | } 110 | 111 | return totalSigOps, nil*/ 112 | } 113 | -------------------------------------------------------------------------------- /frontend/src/i18n/no.json: -------------------------------------------------------------------------------- 1 | { 2 | "generic": { 3 | "retry": "Prøv igjen", 4 | "back_to_wallet": "tilbake til lommebok", 5 | "close": "Lukk" 6 | }, 7 | "welcome": { 8 | "alreadyrunning": "One-Click Miner kjører allerede. Du kan ikke starte et nytt program mens det er et annet i gang.", 9 | "click_button_to_start": "Klikk på knappen under for å gjenoppta utvinning.", 10 | "startmining": "Start å utvinne!", 11 | "makeapassword": "Lag et passord. Ikke mist det.", 12 | "password": "Passord", 13 | "confirmpassword": "Bekreft passord", 14 | "password_cannot_be_empty": "Passord kan ikke stå tomt", 15 | "password_mismatch": "Passordene er ikke identiske", 16 | "error_initializing": "Noe feilet under initialiseringen av lommeboka" 17 | }, 18 | "checks": { 19 | "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)", 20 | "checks_failed": "Kontrolleringen feilet", 21 | "checking_mining_software": "Kontrollerer utvinnings-programmet...", 22 | "rapidfail": "Sjekker for hyppige feil", 23 | "compatibility": "Sjekker CPU kompatibilitet...", 24 | "installing_miners": "Installerer utvinnings-programvare..." 25 | }, 26 | "mining": { 27 | "spendable_balance": "Tilgjengelig saldo", 28 | "still_maturing": "Under modning", 29 | "pending_pool_payout": "Avventende pool-utbetaling", 30 | "waiting_for_miners": "Venter på at utvinnere starter", 31 | "expected_earnings_24h": "Forventet gevinst (24t)", 32 | "estimating": "Estimerer", 33 | "stop_mining": "Stopp utvinning" 34 | }, 35 | "sending": { 36 | "send_all_to": "Send alle utvunnede kryptomynter til", 37 | "youre_sending_x_to": "Du sender {beløp} til", 38 | "youre_sending_x_in_y_txs_to": "Du sender {receivedBalance} i {receivedTxCount} transaksjon til", 39 | "receiver_address": "Mottaksadresse", 40 | "wallet_password": "Lommebokpassord", 41 | "send": "Send", 42 | "coins_sent": "Dine kryptomynter er sendt!", 43 | "view_trans_plural": "Se transaksjoner", 44 | "view_trans_singular": "Se transaksjon", 45 | "failed_to_send": "Sending av kryptomynter mislyktes", 46 | "password_required": "Passord for lommebok er påkrevd", 47 | "invalid_address": "Ugyldig addresse", 48 | "script_failure": "Script feilet", 49 | "could_not_calculate_fee": "Kunne ikke regne ut beløp", 50 | "insufficient_funds": "Ikke tilstrekkelige midler", 51 | "sign_failed" : "Kan ikke signere transaksjonen. Sjekk passordet ditt", 52 | "send_failed": "Kunne ikke gjennomføre transaksjonen. Sjekk debug.log for mer informasjon" 53 | }, 54 | "settings": { 55 | "enable_debug": "Aktiver debugging", 56 | "enable_debug_sub": "Inkluder OCMs utdata i feilsøkingsloggen. Kan gjøre loggene en del større", 57 | "auto_start": "Automatisk start", 58 | "auto_start_sub": "Start One-Click Miner når du logger på maskinen", 59 | "closed_source": "Bruk utvinningsprogramvare med stengt kildekode", 60 | "closed_source_sub": "Bedre hashrate, men ukjente utvinnerprogramvareutvikleren kan ha en avgift for å utvinne", 61 | "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.", 62 | "save_n_restart": "Lagre og start på nytt" 63 | }, 64 | "tabbar": { 65 | "wallet": "Lommebok", 66 | "send_coins": "Send kryptomynter (DOGE)", 67 | "settings": "Innstillinger" 68 | }, 69 | "tracking": { 70 | "update_available": "Oppdatering tilgjengelig", 71 | "tracking_enabled": "Du deler brukerstatistikken anonymt", 72 | "disable_tracking": "Deaktiver", 73 | "tracking_disabled": "Du deler ikke brukerstatistikk", 74 | "enable_tracking": "Aktiver tracking for å hjelpe oss å bedre din brukeropplevelse", 75 | "report_issue": "Rapporter et problem" 76 | }, 77 | "update": { 78 | "new_version_available": "Ny versjon er tilgjengelig", 79 | "download": "Last ned" 80 | } 81 | } -------------------------------------------------------------------------------- /keyfile/keyfile.go: -------------------------------------------------------------------------------- 1 | package keyfile 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | 10 | "github.com/btcsuite/btcd/btcec" 11 | "github.com/btcsuite/btcd/txscript" 12 | "github.com/btcsuite/btcutil" 13 | "github.com/btcsuite/btcutil/base58" 14 | "github.com/vertiond/verthash-one-click-miner/logging" 15 | "github.com/vertiond/verthash-one-click-miner/networks" 16 | "github.com/vertiond/verthash-one-click-miner/util" 17 | "golang.org/x/crypto/nacl/secretbox" 18 | "golang.org/x/crypto/scrypt" 19 | ) 20 | 21 | // KeyFileValid returns true if there is a valid initialized keyfile 22 | // available 23 | func KeyFileValid() bool { 24 | return len(loadPublicKey()) == 33 25 | } 26 | 27 | func CreateKeyFile(pass string) error { 28 | filename := keyFile() 29 | 30 | // Create random key 31 | priv32 := new([32]byte) 32 | _, err := rand.Read(priv32[:]) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | // Derive pubkey 38 | _, pub := btcec.PrivKeyFromBytes(btcec.S256(), priv32[:]) 39 | 40 | salt := new([24]byte) // salt for scrypt / nonce for secretbox 41 | dk32 := new([32]byte) // derived key from scrypt 42 | 43 | //get 24 random bytes for scrypt salt (and secretbox nonce) 44 | _, err = rand.Read(salt[:]) 45 | if err != nil { 46 | return err 47 | } 48 | // next use the pass and salt to make a 32-byte derived key 49 | dk, err := scrypt.Key([]byte(pass), salt[:], 16384, 8, 1, 32) 50 | if err != nil { 51 | return err 52 | } 53 | copy(dk32[:], dk[:]) 54 | 55 | enckey := append(salt[:], secretbox.Seal(nil, priv32[:], salt, dk32)...) 56 | return os.WriteFile(filename, append(pub.SerializeCompressed(), enckey...), 0600) 57 | } 58 | 59 | func keyFile() string { 60 | return filepath.Join(util.DataDirectory(), "dogekeyfile.hex") 61 | } 62 | 63 | func loadPublicKey() []byte { 64 | filename := keyFile() 65 | b, err := os.ReadFile(filename) 66 | if err != nil { 67 | logging.Infof("Error reading keyfile: %s", err.Error()) 68 | return []byte{} 69 | } 70 | if len(b) != 105 { 71 | logging.Infof("Keyfile had wrong length. Expected 129, got %d", len(b)) 72 | return []byte{} 73 | } 74 | ret := make([]byte, 33) 75 | copy(ret, b[:33]) 76 | b = nil 77 | return ret 78 | } 79 | 80 | func GetPublicKey() []byte { 81 | pub := loadPublicKey() 82 | return pub 83 | } 84 | 85 | func GetAddress() string { 86 | pub := loadPublicKey() 87 | return base58.CheckEncode(btcutil.Hash160(pub), networks.Active.Base58P2PKHVersion) 88 | } 89 | 90 | func GetScript() ([]byte, error) { 91 | pub := loadPublicKey() 92 | return txscript.NewScriptBuilder().AddOp(txscript.OP_DUP). 93 | AddOp(txscript.OP_HASH160).AddData(btcutil.Hash160(pub)). 94 | AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).Script() 95 | } 96 | 97 | func LoadPrivateKey(password string) ([]byte, error) { 98 | filename := keyFile() 99 | keyfile, err := os.ReadFile(filename) 100 | if err != nil { 101 | return []byte{}, err 102 | } 103 | if len(keyfile) != 105 { 104 | return []byte{}, fmt.Errorf("Key length error for %s ", filename) 105 | } 106 | 107 | enckey := keyfile[33:] 108 | // enckey is actually encrypted, get derived key from pass and salt 109 | // first extract salt 110 | salt := new([24]byte) // salt (also nonce for secretbox) 111 | dk32 := new([32]byte) // derived key array 112 | copy(salt[:], enckey[:24]) // first 24 bytes are scrypt salt/box nonce 113 | 114 | dk, err := scrypt.Key([]byte(password), salt[:], 16384, 8, 1, 32) // derive key 115 | if err != nil { 116 | return []byte{}, err 117 | } 118 | copy(dk32[:], dk[:]) // copy into fixed size array 119 | 120 | // nonce for secretbox is the same as scrypt salt. Seems fine. Really. 121 | priv, worked := secretbox.Open(nil, enckey[24:], salt, dk32) 122 | if !worked { 123 | return []byte{}, fmt.Errorf("Decryption failed for %s ", filename) 124 | } 125 | 126 | return priv, nil 127 | } 128 | 129 | func TestPassword(password string) bool { 130 | priv, err := LoadPrivateKey(password) 131 | if err != nil { 132 | return false 133 | } 134 | _, pub := btcec.PrivKeyFromBytes(btcec.S256(), priv) 135 | return bytes.Equal(loadPublicKey(), pub.SerializeCompressed()) 136 | } 137 | -------------------------------------------------------------------------------- /frontend/src/i18n/hi.json: -------------------------------------------------------------------------------- 1 | { 2 | "generic" : { 3 | "retry" : "पुन: प्रयास", 4 | "back_to_wallet" : "वापस वॉलेट में", 5 | "close" : "बंद करे" 6 | }, 7 | "welcome" : { 8 | "alreadyrunning" : "वन-क्लिक माइनर पहले से ही चल रहा है। आप इसे एक से अधिक बार नहीं चला सकते।", 9 | "click_button_to_start" : "फिर से खनन शुरू करने के लिए नीचे दिए गए बटन पर क्लिक करें।", 10 | "startmining" : "खनन शुरू करो!", 11 | "makeapassword" : "एक पासवर्ड बनाएं। इसे खोना नहीं है।", 12 | "password" : "पारण शब्द", 13 | "confirmpassword" : "पासवर्ड की पुष्टि कीजिये", 14 | "password_cannot_be_empty" : "पासवर्ड खाली नहीं हो सकता", 15 | "password_mismatch" : "पासवर्ड मेल नहीं खाते", 16 | "error_initializing" : "वॉलेट को शुरू करने में कुछ गलत हुआ" 17 | }, 18 | "checks" : { 19 | "prerequisite" : "एक शर्त लगाई जा रही है। आप एक पॉपअप अनुमति के लिए पूछ सकते हैं (बस टास्कबार में निमिष हो सकता है)", 20 | "checks_failed" : "जाँच विफल", 21 | "checking_mining_software" : "खनन सॉफ्टवेयर की जाँच करना...", 22 | "rapidfail" : "तेजी से विफलता की घटनाओं के लिए जाँच", 23 | "compatibility" : "ग्राफिक्स प्रोसेसिंग यूनिट संगतता की जाँच करना...", 24 | "installing_miners" : "खनन सॉफ्टवेयर स्थापित करना..." 25 | }, 26 | "mining" : { 27 | "spendable_balance" : "व्यय करने योग्य शेष", 28 | "still_maturing" : "अभी भी परिपक्व हो रहा है", 29 | "pending_pool_payout" : "लंबित पूल भुगतान", 30 | "waiting_for_miners" : "खनिकों के शुरू होने का इंतजार", 31 | "expected_earnings_24h" : "अपेक्षित कमाई (चौबीस घंटे)", 32 | "estimating" : "का आकलन", 33 | "stop_mining" : "खनन बंद करो" 34 | }, 35 | "sending" : { 36 | "send_all_to" : "करने के लिए अपने सभी खनन सिक्के भेजें", 37 | "youre_sending_x_to" : "आप भेज रहे हैं {receivedBalance} सेवा मेरे", 38 | "youre_sending_x_in_y_txs_to" : "आप भेज रहे हैं {receivedBalance} में {receivedTxCount} लेन-देन", 39 | "receiver_address" : "रिसीवर का पता", 40 | "wallet_password" : "वॉलेट पासवर्ड", 41 | "send" : "भेजना", 42 | "coins_sent" : "आपके सिक्के भेजे हैं", 43 | "view_trans_plural" : "लेन-देन देखें", 44 | "view_trans_singular" : "लेन-देन देखें", 45 | "failed_to_send" : "अपने सिक्के भेजने में विफल", 46 | "password_required" : "वॉलेट पासवर्ड की आवश्यकता है", 47 | "invalid_address" : "गलत पता", 48 | "script_failure" : "स्क्रिप्ट की विफलता", 49 | "could_not_calculate_fee" : "शुल्क की गणना नहीं कर सका", 50 | "insufficient_funds" : "अपर्याप्त कोष", 51 | "sign_failed" : "लेन-देन पर हस्ताक्षर करने में असमर्थ। अपना पासवर्ड जांचें", 52 | "send_failed" : "लेनदेन भेजने में असमर्थ। अधिक जानकारी के लिए debug.log की जाँच करें" 53 | }, 54 | "settings" : { 55 | "enable_debug" : "डिबगिंग सक्षम करें", 56 | "enable_debug_sub" : "डिबग लॉग में माइनर के कंसोल आउटपुट को शामिल करें। अपने लॉग को काफी बड़ा कर सकते हैं", 57 | "auto_start" : "ऑटो स्टार्ट", 58 | "auto_start_sub" : "जब आप अपने कंप्यूटर में लॉग इन करते हैं तो वन-क्लिक माइनर शुरू करें", 59 | "closed_source" : "बंद-स्रोत खनिक का उपयोग करें", 60 | "closed_source_sub" : "बेहतर हैशेट, लेकिन अघोषित खनिक जो एक डेवलपर के शुल्क को बढ़ाते हैं", 61 | "closed_source_warning" : "आपने बंद स्रोत खननकर्ता का उपयोग करने के लिए चुना है। Vertcoin इन खनिकों का समर्थन या समर्थन नहीं करता है। उनकी सामग्री पर उनका ऑडिट नहीं किया जा सकता है और इसमें आपके कंप्यूटर को नुकसान पहुंचाने वाले कार्य हो सकते हैं।", 62 | "save_n_restart" : "सहेजें और पुनरारंभ करें" 63 | }, 64 | "tabbar" : { 65 | "wallet" : "बटुआ", 66 | "send_coins" : "सिक्के भेजें (DOGE)", 67 | "settings" : "सेटिंग्स" 68 | }, 69 | "tracking": { 70 | "update_available" : "अपडेट उपलब्ध", 71 | "tracking_enabled" : "आप गुमनाम रूप से उपयोग के आंकड़े साझा कर रहे हैं", 72 | "disable_tracking" : "अक्षम", 73 | "tracking_disabled" : "आप उपयोग के आंकड़े साझा नहीं कर रहे हैं", 74 | "enable_tracking" : "अपने अनुभव को बेहतर बनाने में हमारी सहायता करने के लिए इन्हें सक्षम करें", 75 | "report_issue" : "मामले की रिपोर्ट करें" 76 | }, 77 | "update" : { 78 | "new_version_available" : "नया संस्करण उपलब्ध है", 79 | "download" : "डाउनलोड" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /payouts/payout.go: -------------------------------------------------------------------------------- 1 | package payouts 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/vertiond/verthash-one-click-miner/util" 8 | ) 9 | 10 | type Payout interface { 11 | GetID() int 12 | GetDisplayName() string 13 | GetTicker() string 14 | GetCoingeckoExchange() string 15 | GetCoingeckoCoinID() string 16 | GetNetworks() []string 17 | } 18 | 19 | // func GetPayouts(testnet bool) []Payout { 20 | // if testnet { 21 | // return []Payout{ 22 | // NewVTCPayout(), 23 | // } 24 | // } 25 | // return []Payout{ 26 | // NewDOGEPayout(), 27 | // NewVTCPayout(), 28 | // NewBTCPayout(), 29 | // NewBCHPayout(), 30 | // NewDASHPayout(), 31 | // NewDGBPayout(), 32 | // NewETHPayout(), 33 | // NewFIROPayout(), 34 | // NewGRSPayout(), 35 | // NewLTCPayout(), 36 | // NewXMRPayout(), 37 | // NewRVNPayout(), 38 | // } 39 | // } 40 | 41 | func GetBitcoinPerUnitCoin(coinID string, coinTicker string, exchange string) float64 { 42 | if coinTicker == "DOGE" { 43 | return GetBitcoinPerUnitDOGE() 44 | } 45 | 46 | jsonPayload := map[string]interface{}{} 47 | err := util.GetJson( 48 | fmt.Sprintf("https://api.coingecko.com/api/v3/exchanges/%s/tickers?coin_ids=%s", exchange, coinID), 49 | &jsonPayload) 50 | if err != nil { 51 | return 0.0 52 | } 53 | jsonTickersArr, ok := jsonPayload["tickers"].([]interface{}) 54 | if !ok { 55 | return 0.0 56 | } 57 | 58 | result := 0.0 59 | fallback_result := 0.0 60 | for _, jsonTickerInfo := range jsonTickersArr { 61 | jsonTickerInfoMap := jsonTickerInfo.(map[string]interface{}) 62 | jsonTickerBase, ok1 := jsonTickerInfoMap["base"] 63 | jsonTickerTarget, ok2 := jsonTickerInfoMap["target"] 64 | if !ok1 || !ok2 { 65 | continue 66 | } 67 | if jsonTickerBase == coinTicker && ( jsonTickerTarget == "BTC" || fallback_result == 0.0 ) { 68 | jsonTickerConvertedLast, ok := jsonTickerInfoMap["converted_last"].(map[string]interface{}) 69 | if ok { 70 | jsonTickerConvertedLastBTC, ok := jsonTickerConvertedLast["btc"].(float64) 71 | if ok { 72 | if fallback_result == 0.0 { 73 | fallback_result = jsonTickerConvertedLastBTC 74 | } 75 | if jsonTickerTarget == "BTC" { 76 | result = jsonTickerConvertedLastBTC 77 | break 78 | } 79 | } 80 | } 81 | } 82 | } 83 | if result == 0.0 && fallback_result != 0.0 { 84 | result = fallback_result 85 | } 86 | 87 | return result 88 | } 89 | 90 | func GetBitcoinPerUnitDOGE() float64 { 91 | jsonPayload := map[string]interface{}{} 92 | err := util.GetJson("https://sochain.com/api/v2/get_price/DOGE/BTC", &jsonPayload) 93 | if err != nil { 94 | return 0.0 95 | } 96 | jsonData, ok := jsonPayload["data"].(map[string]interface{}) 97 | if !ok { 98 | return 0.0 99 | } 100 | jsonPriceArr, ok := jsonData["prices"].([]interface{}) 101 | if !ok { 102 | return 0.0 103 | } 104 | 105 | result := 0.0 106 | for _, jsonPriceInfo := range jsonPriceArr { 107 | jsonPriceInfoMap := jsonPriceInfo.(map[string]interface{}) 108 | jsonPriceBase, ok := jsonPriceInfoMap["price_base"] 109 | if !ok { 110 | continue 111 | } 112 | // Could pull from Bittrex or Binance at any given time, 113 | // whichever one happens to be listed first. 114 | // Doesn't matter which, as long as "price_base" is "BTC". 115 | if jsonPriceBase == "BTC" { 116 | jsonExchangePrice, ok := jsonPriceInfoMap["price"].(string) 117 | if ok { 118 | result, _ = strconv.ParseFloat(jsonExchangePrice, 64) 119 | } 120 | break 121 | } 122 | } 123 | return result 124 | } 125 | 126 | //func GetBitcoinPerUnitCoin(coinID string, coinTicker string, exchange string) float64 { 127 | // jsonPayload := map[string]interface{}{} 128 | // err := util.GetJson(fmt.Sprintf( 129 | // "https://api.cryptonator.com/api/ticker/%s-btc", 130 | // strings.ToLower(coinTicker)), 131 | // &jsonPayload) 132 | // if err != nil { 133 | // return 0.0 134 | // } 135 | // jsonTicker, ok := jsonPayload["ticker"].(map[string]interface{}) 136 | // if !ok { 137 | // return 0.0 138 | // } 139 | // jsonTickerPrice, ok := jsonTicker["price"].(string) 140 | // if !ok { 141 | // return 0.0 142 | // } 143 | // result, _ := strconv.ParseFloat(jsonTickerPrice, 64) 144 | // return result 145 | //} 146 | -------------------------------------------------------------------------------- /frontend/src/i18n/sl.json: -------------------------------------------------------------------------------- 1 | { 2 | "generic" : { 3 | "retry" : "Poskusi znova", 4 | "back_to_wallet" : "Nazaj v denarnico", 5 | "close":"Zapri" 6 | }, 7 | "welcome" : { 8 | "alreadyrunning" : "One-Click Miner že teče. Ne morete ga zagnati več kot enkrat.", 9 | "click_button_to_start" : "Za ponoven začetek rudarjenja pritisni spodnji gumb.", 10 | "startmining" : "Začni rudariti!", 11 | "makeapassword" : "Ustvari geslo. Ne izgubi ga!", 12 | "password" : "Geslo", 13 | "confirmpassword" : "Potrdi geslo", 14 | "password_cannot_be_empty" : "Polje geslo ne sme biti prazno", 15 | "password_mismatch" : "Geslo se ne ujema", 16 | "error_initializing" : "Napaka pri nalaganju denarnice" 17 | }, 18 | "checks" : { 19 | "prerequisite" : "Nameščanje predpogojnih datotek. Morda boste videli pojavno okno, ki zahteva dovoljenja (lahko samo utripa v opravilni vrstici)", 20 | "checks_failed" : "Preverjanje neuspešno", 21 | "checking_mining_software" : "Preverjanje programske opreme za rudarstvo ...", 22 | "rapidfail" : "Preverjanje hitrih okvar", 23 | "compatibility" : "Preverjanje združljivosti grafične kartice ...", 24 | "installing_miners" : "Nameščanje rudarske programske opreme ..." 25 | }, 26 | "mining" : { 27 | "spendable_balance" : "Razpoložljivi znesek", 28 | "still_maturing" : "v fazi zorenja", 29 | "pending_pool_payout" : "čakajoče pool izplačilo", 30 | "waiting_for_miners" : "Čakanje na rudarsko programsko opremo za začetek dela", 31 | "expected_earnings_24h" : "Pričakovani zaslužek (24h)", 32 | "estimating" : "ocenjevanje", 33 | "stop_mining" : "Prekini rudarjenje" 34 | }, 35 | "sending" : { 36 | "send_all_to" : "Pošlji vse izrudarjene kovance na", 37 | "youre_sending_x_to" : "Pošiljaš {receivedBalance} na", 38 | "youre_sending_x_in_y_txs_to" : "Pošiljaš {receivedBalance} v {receivedTxCount} transakcij na", 39 | "receiver_address" : "Naslov prejemnika", 40 | "wallet_password" : "Geslo denarnice", 41 | "send" : "Pošlji", 42 | "coins_sent" : "Kovanci so poslani!", 43 | "view_trans_plural" : "Ogled transakcij", 44 | "view_trans_singular" : "Ogled transakcije", 45 | "failed_to_send" : "Kovancev ni bilo mogoče poslati", 46 | "password_required" : "Potrebno je geslo za denarnico", 47 | "invalid_address" : "Napačen naslov", 48 | "script_failure" : "Napaka skripte", 49 | "could_not_calculate_fee" : "Ni bilo mogoče izračunati pristojbine", 50 | "insufficient_funds" : "Nezadostna sredstva", 51 | "sign_failed":"Transakcije ni mogoče podpisati. Preverite geslo", 52 | "send_failed" : "Transakcije ni bilo mogoče poslati. Poglej debug.log za več informacij" 53 | }, 54 | "settings" : { 55 | "enable_debug" : "Omogoči iskanje napak", 56 | "enable_debug_sub" : "Omogoči izvoz dnevnikov konzol rudarske programske opreme v dnevnik za odstranjevanje hroščev. Velikost dnevnika se lahko znatno poveča", 57 | "auto_start" : "Samodejni zagon", 58 | "auto_start_sub" : "Zaženi One-Click Miner, ko se prijaviš v računalnik.", 59 | "closed_source" : "Uporabi zaprto programsko opremo za rudarstvo", 60 | "closed_source_sub" : "Boljši hashrate, vendar neocenjena programska opema, ki lahko vključuje pristojbino za razvojne inženirje", 61 | "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.", 62 | "save_n_restart" : "Shrani & Znova zaženi" 63 | }, 64 | "tabbar" : { 65 | "wallet" : "Denarnica", 66 | "send_coins" : "Pošlji kovance (DOGE)", 67 | "settings" : "Nastavitve" 68 | }, 69 | "tracking": { 70 | "update_available" : "Posodobitev je na voljo", 71 | "tracking_enabled" : "Anonimno izmenjujete statistične podatke o uporabi", 72 | "disable_tracking" : "Onemogoči", 73 | "tracking_disabled" : "Ne delite statistike o uporabi", 74 | "enable_tracking" : "Omogočite jih, da bi nam pomagali izboljšati vaše izkušnje", 75 | "report_issue" : "Prijavite težavo" 76 | }, 77 | "update" : { 78 | "new_version_available" : "Na voljo je nova različica", 79 | "download" : "Prenos" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /frontend/src/i18n/hr.json: -------------------------------------------------------------------------------- 1 | { 2 | "generic" : { 3 | "retry" : "Pokušajte ponovno", 4 | "back_to_wallet" : "Natrag na novčanik", 5 | "close" : "Zatvoriti" 6 | }, 7 | "welcome" : { 8 | "alreadyrunning" : "One-Click Miner već radi. Ne možete ga pokrenuti više od jednom.", 9 | "click_button_to_start" : "Pritisnite dugme ispod da ponovno započnete sa rudarenjem.", 10 | "startmining" : "Započnite sa rudarenjem!", 11 | "makeapassword" : "Kreirajte lozinku. Nemojte ju izgubiti.", 12 | "password" : "Lozinka", 13 | "confirmpassword" : "Potvrdite lozinku", 14 | "password_cannot_be_empty" : "Polje za lozinku ne može biti prazno", 15 | "password_mismatch" : "Lozinke se ne podudaraju", 16 | "error_initializing" : "Pogreška kod učitavanja novčanika" 17 | }, 18 | "checks" : { 19 | "prerequisite" : "Skidanje preduvjetnih datoteka. Mogućnost iskakanja skočnog prozora za dopuštenja (može treperiti u alatnoj traci)", 20 | "checks_failed" : "Provjera neuspješna", 21 | "checking_mining_software" : "Provjera softvera za rudarenje...", 22 | "rapidfail" : "Provjera učestalosti neuspješnih radnji", 23 | "compatibility" : "Provjera kompatibilnosti grafičke kartice...", 24 | "installing_miners" : "Instalacija softvera za rudarenje..." 25 | }, 26 | "mining" : { 27 | "spendable_balance" : "Dostupan iznos", 28 | "still_maturing" : "nedozrelo", 29 | "pending_pool_payout" : "očekivana pool isplata", 30 | "waiting_for_miners" : "Čekanje da softver za rudarenje započne sa radom", 31 | "expected_earnings_24h" : "Očekivana zarada(24h)", 32 | "estimating" : "Vršim procjenu", 33 | "stop_mining" : "Zaustavi rudarenje" 34 | }, 35 | "sending" : { 36 | "send_all_to" : "Pošalji sve izrudarene novčiće na", 37 | "youre_sending_x_to" : "Šaljete {receivedBalance} na", 38 | "youre_sending_x_in_y_txs_to" : "Šaljete {receivedBalance} u {receivedTxCount} transakcija na", 39 | "receiver_address" : "Adresa primatelja", 40 | "wallet_password" : "Lozinka novčanika", 41 | "send" : "Pošalji", 42 | "coins_sent" : "Vaši novčići su poslani!", 43 | "view_trans_plural" : "Vidi transakciju", 44 | "view_trans_singular" : "Vidi transakcije", 45 | "failed_to_send" : "Neuspješno slanje vaših novčića", 46 | "password_required" : "Potrebna je lozinka novčanika", 47 | "invalid_address" : "Nepostojeća adresa", 48 | "script_failure" : "Greška skripte", 49 | "could_not_calculate_fee" : "Nije moguće izračunati naknadu", 50 | "insufficient_funds" : "Nedostatna sredstva", 51 | "sign_failed" : "Nije moguće potpisati transakciju. Provjerite zaporku", 52 | "send_failed" : "Nije moguće poslati transakciju. Provjerite debug.log za više informacija" 53 | }, 54 | "settings" : { 55 | "enable_debug" : "Omogući otklanjanje bugova", 56 | "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", 57 | "auto_start" : "Automatski početak", 58 | "auto_start_sub" : "Pokreni One-Click Miner kada se logiraš na računalo", 59 | "closed_source" : "Koristi softver za rudarenje zatvorenog koda", 60 | "closed_source_sub" : "Bolji hashrate, ali neocijenjeni softver za rudarenje može uključivati naknadu za razvojne inženjere", 61 | "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.", 62 | "save_n_restart" : "Spremi i ponovno pokreni" 63 | }, 64 | "tabbar" : { 65 | "wallet" : "Novčanik", 66 | "send_coins" : "Pošalji novčiće (DOGE)", 67 | "settings" : "Postavke" 68 | }, 69 | "tracking": { 70 | "update_available" : "Nova inačica je dostupna", 71 | "tracking_enabled" : "Anonimno dijelite vašu statistiku o korištenju", 72 | "disable_tracking" : "Onemogući", 73 | "tracking_disabled" : "Ne dijelite statistiku o korištenju", 74 | "enable_tracking" : "Omogućite i pomognite nam da unaprijedimo korisničko iskustvo", 75 | "report_issue" : "Prijavite problem" 76 | }, 77 | "update" : { 78 | "new_version_available" : "Nova verzija je dostupna", 79 | "download" : "Skidanje" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /frontend/src/assets/css/main.css: -------------------------------------------------------------------------------- 1 | #app { 2 | font-family: 'Montserrat', Helvetica, Arial, sans-serif; 3 | -webkit-font-smoothing: antialiased; 4 | -moz-osx-font-smoothing: grayscale; 5 | text-align: center; 6 | color: #eee; 7 | -moz-user-select: none; 8 | -webkit-user-select: none; 9 | -ms-user-select:none; 10 | user-select:none; 11 | -o-user-select:none; 12 | margin: 0px; 13 | padding: 0px; 14 | font-size: 14px; 15 | } 16 | 17 | select { 18 | font-family: 'Montserrat', Helvetica, Arial, sans-serif; 19 | border: 2px solid #eee !important; 20 | background: #191b1c !important; 21 | color: #eee !important; 22 | -webkit-appearance: none; 23 | padding: 4px 10px; 24 | } 25 | 26 | 27 | /* ubuntu-regular - latin */ 28 | /* montserrat-regular - latin */ 29 | @font-face { 30 | font-family: 'Montserrat'; 31 | font-style: normal; 32 | font-weight: 400; 33 | src: url('../fonts/montserrat-v13-latin-regular.eot'); /* IE9 Compat Modes */ 34 | src: local('Montserrat Regular'), local('Montserrat-Regular'), 35 | url('../fonts/montserrat-v13-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ 36 | url('../fonts/montserrat-v13-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */ 37 | url('../fonts/montserrat-v13-latin-regular.woff') format('woff'), /* Modern Browsers */ 38 | url('../fonts/montserrat-v13-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */ 39 | url('../fonts/montserrat-v13-latin-regular.svg#Montserrat') format('svg'); /* Legacy iOS */ 40 | } 41 | 42 | 43 | html { 44 | height: 100%; 45 | overflow: hidden; 46 | background-color: #8b7426; 47 | background-size: 20px 20px; 48 | padding: 0px; margin: 0px; 49 | 50 | } 51 | 52 | body { 53 | padding: 0px; margin: 0px; 54 | } 55 | 56 | .logo { 57 | width: 16em; 58 | } 59 | 60 | .col-6 { 61 | width: 50%; 62 | float: left; 63 | } 64 | 65 | div.col-286 { 66 | margin: 0px; 67 | padding-left: 200px; 68 | padding-right: 200px; 69 | height: 100%; 70 | width: 100%; 71 | display: table-cell; 72 | vertical-align: middle; 73 | text-align: center; 74 | } 75 | 76 | div.col-settings { 77 | height: 266px; 78 | width: 80%; 79 | margin-left: 10%; 80 | vertical-align: middle; 81 | text-align: center; 82 | overflow-y: auto; 83 | display: flex; 84 | flex-basis: 1; 85 | } 86 | 87 | div.col-settings-sub { 88 | padding: 20px; 89 | width: 50%; 90 | flex-grow: 1; 91 | } 92 | 93 | div.col-286.height-100 { 94 | height: 100px; 95 | display: inline; 96 | } 97 | 98 | 99 | div.col-wide { 100 | margin: 0px; 101 | padding-left: 50px; 102 | padding-right: 50px; 103 | height: 100%; 104 | width: 100%; 105 | display: table-cell; 106 | vertical-align: middle; 107 | text-align: center; 108 | } 109 | div.settings-container { 110 | height: 348px; 111 | width: 100%; 112 | text-align: center; 113 | } 114 | div.container { 115 | height: 348px; 116 | width: 100%; 117 | display: table; 118 | text-align: center; 119 | } 120 | 121 | a.button:hover { 122 | opacity: 1.0; 123 | transition: 500ms; 124 | } 125 | a.button { 126 | opacity: 0.6; 127 | line-height: 45px; 128 | background: #202225; 129 | max-width: 286px; 130 | height: 45px; 131 | margin: 0 auto; 132 | display: block; 133 | color: white; 134 | z-index: 500; 135 | box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.15); 136 | cursor: pointer; 137 | font-weight: 400 !important; 138 | text-align: center; 139 | border-radius: 5px; 140 | } 141 | 142 | a.link, a.inlineLink { 143 | opacity: 0.6; 144 | color: #eee; 145 | cursor: pointer; 146 | margin: 0 auto; 147 | text-align: center; 148 | } 149 | 150 | a.link { 151 | display: block; 152 | } 153 | 154 | a.inlineLink { 155 | display: inline; 156 | } 157 | 158 | input[type='text'], input[type='password'] { 159 | border: 0px; 160 | background-color: #202225; 161 | color: white; 162 | border-radius: 5px; 163 | line-height: 45px; 164 | padding: 0px 10px; 165 | height: 45px; 166 | width: 400px; 167 | margin: 0 auto; 168 | font-family: 'Montserrat', Helvetica, Arial, sans-serif; 169 | font-size: 14px; 170 | } 171 | 172 | p.error { 173 | color: #900000; 174 | } 175 | input.error { 176 | border: 1px solid #900000; 177 | } 178 | input.success { 179 | border: 1px solid #eee; 180 | } 181 | 182 | input:-moz-focusring { 183 | color:transparent; 184 | text-shadow:0 0 0 #000; /* your normal text color here */ 185 | } 186 | input:-moz-focusring * { 187 | color:#000; /* your normal text color here */ 188 | text-shadow:none; 189 | } -------------------------------------------------------------------------------- /frontend/src/i18n/ro.json: -------------------------------------------------------------------------------- 1 | { 2 | "generic" : { 3 | "retry" : "Încearcă din nou", 4 | "back_to_wallet" : "Înapoi la portofel", 5 | "close" : "Închide" 6 | }, 7 | "welcome" : { 8 | "alreadyrunning" : "Minerul One-Click este deja pornit. Poți să îl pornești doar o singură dată.", 9 | "click_button_to_start" : "Apasă pe butonul de mai jos pentru a reîncepe să minezi.", 10 | "startmining" : "Începe să minezi!", 11 | "makeapassword" : "Creează o parolă. Nu o pierde.", 12 | "password" : "Parolă", 13 | "confirmpassword" : "Confirmă parola", 14 | "password_cannot_be_empty" : "Câmpul Parolă nu poate fi gol", 15 | "password_mismatch" : "Parolele trebuie să fie identice", 16 | "error_initializing" : "A apărut o eroare la inițializarea portofelului." 17 | }, 18 | "checks" : { 19 | "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)", 20 | "checks_failed" : "Verificări eșuate", 21 | "checking_mining_software" : "Se verifică software-ul de minare...", 22 | "rapidfail" : "Se verifică dacă există erori rapide (i.e. programul se oprește imediat după ce a pornit)", 23 | "compatibility" : "Se verifică compatibilitatea cu placa grafică...", 24 | "installing_miners" : "Se instalează software-ul de minare..." 25 | }, 26 | "mining" : { 27 | "spendable_balance" : "Balanță disponibilă", 28 | "still_maturing" : "în curs de maturizare", 29 | "pending_pool_payout" : "plată de la pool în curs de efectuare", 30 | "waiting_for_miners" : "Se așteaptă pornirea minerilor", 31 | "expected_earnings_24h" : "Câștiguri așteptate (24h)", 32 | "estimating" : "se estimează", 33 | "stop_mining" : "Oprește minarea" 34 | }, 35 | "sending" : { 36 | "send_all_to" : "Trimite toate monedele minate către", 37 | "youre_sending_x_to" : "Vei trimite {receivedBalance} către", 38 | "youre_sending_x_in_y_txs_to" : "Vei trimite {receivedBalance} în {receivedTxCount} tranzacții către", 39 | "receiver_address" : "Adresa destinatarului", 40 | "wallet_password" : "Parola portofelului", 41 | "send" : "Trimite", 42 | "coins_sent" : "Monedele au fost trimise!", 43 | "view_trans_plural" : "Vizualizare tranzacții", 44 | "view_trans_singular" : "Vizualizare tranzacție", 45 | "failed_to_send" : "Monedele nu au putut fi trimise!", 46 | "password_required" : "Trebuie să introduci parola portofelului", 47 | "invalid_address" : "Adresă invalidă", 48 | "script_failure" : "Eroare de script", 49 | "could_not_calculate_fee" : "Taxa pe tranzacție nu a putut fi calculată", 50 | "insufficient_funds" : "Fonduri insuficiente", 51 | "sign_failed":"Imposibil de semnat tranzacția. Verificați parola", 52 | "send_failed" : "Nu se poate trimite tranzacția. Verifică fișierul debug.log pentru informații suplimentare" 53 | }, 54 | "settings" : { 55 | "enable_debug" : "Activează depanarea", 56 | "enable_debug_sub" : "Include textul din consola minerului în logul de depanare. Aceast lucru poate duce la o creștere semnificativă a dimensiunii logurilor", 57 | "auto_start" : "Pornire automată", 58 | "auto_start_sub" : "Pornește Minerul One-Click când te loghezi pe calculator", 59 | "closed_source" : "Folosește mineri closed-source", 60 | "closed_source_sub" : "Au hashrate mai bun, dar sunt mineri neauditați care trimit o taxă către dezvoltator", 61 | "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.", 62 | "save_n_restart" : "Salvează și repornește" 63 | }, 64 | "tabbar" : { 65 | "wallet" : "Portofel", 66 | "send_coins" : "Trimite monede (DOGE)", 67 | "settings" : "Setări" 68 | }, 69 | "tracking": { 70 | "update_available" : "Actualizare disponibilă", 71 | "tracking_enabled" : "Împărtășești statistici de utilizare în mod anonim", 72 | "disable_tracking" : "Dezactivează", 73 | "tracking_disabled" : "Nu împărtășești statistici de utilizare", 74 | "enable_tracking" : "Activează această setare pentru a ne ajuta să îți oferim o experiență mai bună", 75 | "report_issue" : "Raportează o problemă" 76 | }, 77 | "update" : { 78 | "new_version_available" : "O nouă versiune este disponibilă", 79 | "download" : "Descarcă" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /frontend/src/i18n/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "generic" : { 3 | "retry" : "Réessayer", 4 | "back_to_wallet" : "Revenir au portefeuille", 5 | "close" : "Fermer" 6 | }, 7 | "welcome" : { 8 | "alreadyrunning" : "One-Click Miner est déjà en cours d'exécution. Vous ne pouvez pas l'exécuter plus d'une fois.", 9 | "click_button_to_start" : "Cliquer sur le bouton ci-dessous pour redémarrer le minage.", 10 | "startmining" : "Démarrer le minage !", 11 | "makeapassword" : "Créer un mot de passe. Ne pas le perdre !", 12 | "password" : "Mot de passe", 13 | "confirmpassword" : "Confirmer le mot de passe", 14 | "password_cannot_be_empty" : "Le mot de passe ne peut être vide", 15 | "password_mismatch" : "Le mot de passe ne correspond pas", 16 | "error_initializing" : "Erreur pendant l'initialisation du portefeuille" 17 | }, 18 | "checks" : { 19 | "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)", 20 | "checks_failed" : "Les vérifications ont echoué", 21 | "checking_mining_software" : "Vérification du logiciel de minage...", 22 | "rapidfail" : "Vérification des occurrences d'échec rapide", 23 | "compatibility" : "Vérification de la compatibilité GPU...", 24 | "installing_miners" : "Installation du logiciel de minage..." 25 | }, 26 | "mining" : { 27 | "spendable_balance" : "Solde disponible", 28 | "still_maturing" : "En cours de consolidation", 29 | "pending_pool_payout" : "Paiement de la pool en attente", 30 | "waiting_for_miners" : "En attente de mineurs pour démarrer", 31 | "expected_earnings_24h" : "Bénéfices attendus (24h)", 32 | "estimating" : "Estimation", 33 | "stop_mining" : "Arrêt du minage" 34 | }, 35 | "sending" : { 36 | "send_all_to" : "Envoyer toute votre cryptomonnaie minée à", 37 | "youre_sending_x_to" : "Vous envoyez {receivedBalance} à", 38 | "youre_sending_x_in_y_txs_to" : "Vous envoyez {receivedBalance} en {receivedTxCount} transactions à", 39 | "receiver_address" : "Adresse du destinataire", 40 | "wallet_password" : "Mot de passe du portefeuille", 41 | "send" : "Envoyer", 42 | "coins_sent" : "Votre cryptomonnaie a été envoyée !", 43 | "view_trans_plural" : "Voir les transactions", 44 | "view_trans_singular" : "Voir la transaction", 45 | "failed_to_send" : "Echec lors de l'envoi de votre cryptomonnaie", 46 | "password_required" : "Le mot de passe du portefeuille est requis", 47 | "invalid_address" : "Adresse invalide", 48 | "script_failure" : "Script défectueux", 49 | "could_not_calculate_fee" : "Calcul des frais impossible", 50 | "insufficient_funds" : "Fonds insuffisants", 51 | "sign_failed" : "Impossible de signer la transaction. Vérifiez votre mot de passe", 52 | "send_failed" : "Transaction impossible. Vérifier debug.log pour plus d'informations" 53 | }, 54 | "settings" : { 55 | "enable_debug" : "Activer le débogage", 56 | "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", 57 | "auto_start" : "Démarrage auto", 58 | "auto_start_sub" : "Démarrer One-Click Miner quand vous vous connectez à votre ordinateur", 59 | "closed_source" : "Utiliser des logiciels de minage propriétaires", 60 | "closed_source_sub" : "Meilleur taux de hachage, mais logiciel de minage ne rétribuant pas les développeurs", 61 | "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.", 62 | "save_n_restart" : "Enregistrer & Redémarrer" 63 | }, 64 | "tabbar" : { 65 | "wallet" : "Portefeuille", 66 | "send_coins" : "Envoyer de la cryptomonnaie (DOGE)", 67 | "settings" : "Paramètres" 68 | }, 69 | "tracking": { 70 | "update_available" : "Mise à jour disponible", 71 | "tracking_enabled" : "Vous partagez anonymement vos statistiques d'utilisation", 72 | "disable_tracking" : "Désactiver", 73 | "tracking_disabled" : "Vous ne partagez pas vos statistiques d'utilisation", 74 | "enable_tracking" : "Activer cette option pour nous permettre d'améliorer votre expérience utilisateur", 75 | "report_issue" : "Signaler un problème" 76 | }, 77 | "update" : { 78 | "new_version_available" : "Nouvelle version disponible", 79 | "download" : "Télécharger" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /frontend/src/i18n/sv.json: -------------------------------------------------------------------------------- 1 | { 2 | "generic": { 3 | "retry": "Försök igen", 4 | "back_to_wallet": "Tillbaka till plånboken", 5 | "close": "Stäng" 6 | }, 7 | "welcome": { 8 | "alreadyrunning": "One-Click Miner har redan startats. Du kan bara köra en instans åt gången.", 9 | "click_button_to_start": "Klicka på knappen nedan för att börja mina igen.", 10 | "startmining": "Börja Mina!", 11 | "makeapassword": "Skapa ett lösenord. Tappa inte bort det.", 12 | "password": "Lösenord", 13 | "confirmpassword": "Bekräfta lösenord", 14 | "password_cannot_be_empty": "Du måste ange ett lösenord", 15 | "password_mismatch": "Lösenorden matchar inte", 16 | "error_initializing": "Något gick fel under initialisering av plånboken" 17 | }, 18 | "checks": { 19 | "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)", 20 | "checks_failed": "Kontrollerna misslyckades", 21 | "checking_mining_software": "Kontrollerar mining mjukvaran...", 22 | "rapidfail": "Kontrollerar för förekomsten av fel", 23 | "compatibility": "Kontrollerar GPU-kompatibilitet...", 24 | "installing_miners": "Installera mining mjukvara...", 25 | "verthash": "Verifierar / skapar Verthash data fil..." 26 | }, 27 | "mining": { 28 | "spendable_balance": "Spenderbart saldo", 29 | "still_maturing": "mognar fortfarande", 30 | "pending_pool_payout": "väntande poolutbetalning", 31 | "pending_payout_info_unavailable": "Var vänlig vänta på utbetalnings information", 32 | "waiting_for_miners": "Väntar på att miningen ska börja", 33 | "expected_earnings_24h": "Förväntade intäkter (24h)", 34 | "estimating": "beräknar", 35 | "stop_mining": "Sluta Mina", 36 | "active_pool": "Aktiv pool", 37 | "pool_fee": "avgift", 38 | "copy_address": "Kopiera din plånboks address", 39 | "payout_information": "Visa utbetalnings information" 40 | }, 41 | "sending": { 42 | "send_all_to": "Skicka dina minade mynt till", 43 | "youre_sending_x_to": "Du skickar {receivedBalance} till", 44 | "youre_sending_x_in_y_txs_to": "Du skickar {receivedBalance} i {receivedTxCount} transaktion till", 45 | "receiver_address": "Mottagaradress", 46 | "wallet_password": "Plånbokslösenord", 47 | "send": "Skicka", 48 | "coins_sent": "Dina mynt har skickats!", 49 | "view_trans_plural": "Visa transaktioner", 50 | "view_trans_singular": "Visa transaktion", 51 | "failed_to_send": "Det gick inte att skicka dina mynt", 52 | "password_required": "Plånbokslösenord krävs", 53 | "invalid_address": "Ogiltig adress", 54 | "script_failure": "Script fel", 55 | "could_not_calculate_fee": "Kunde inte beräkna avgift", 56 | "insufficient_funds": "Otillräckliga saldo", 57 | "sign_failed": "Det gick inte att skriva under transaktion. Kontrollera ditt lösenord", 58 | "send_failed": "Det gick inte att skicka transaktion. Kontrollera debug.log filen för mer information" 59 | }, 60 | "settings": { 61 | "enable_debug": "Aktivera felsökning", 62 | "enable_debug_sub": "Inkludera mining mjukvarans utdata i felsökningsloggen. Kan gör att loggfilen växer snabbt", 63 | "auto_start": "Autostart", 64 | "auto_start_sub": "Starta One-Click Miner när du loggar in på din dator", 65 | "closed_source": "Använd mining mjukvara med stängd källkod (Proprietär programvara)", 66 | "closed_source_sub": "Ger bättre hashrate, men källkoden är ej granskad och mjukvaran har en avgift till utvecklaren", 67 | "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.", 68 | "enable_integrated": "Använd integrerat grafikkort (iGPU) för att mina", 69 | "enable_integrated_sub": "Kommer att använda integrerat grafikkort (om möjligt)", 70 | "testnet": "Testnet läge", 71 | "testnet_sub": "Aktivera testnet läge. Du kommer inte mina riktiga mynt.", 72 | "skipverthashverify": "Verifiera inte verthash under start", 73 | "skipverthashverify_sub": "Kommer inte att verifiera verthash data file under uppstart (rekomenderas inte)", 74 | "save_n_restart": "Spara & starta om", 75 | "pool": "Mining pool" 76 | }, 77 | "tabbar": { 78 | "wallet": "Plånbok", 79 | "send_coins": "Skicka mynt (DOGE)", 80 | "settings": "Inställningar" 81 | }, 82 | "tracking": { 83 | "update_available": "Uppdatering tillgänglig", 84 | "tracking_enabled": "Du delar anonym användningsstatistik", 85 | "disable_tracking": "Inaktivera", 86 | "tracking_disabled": "Du delar inte användningsstatistik", 87 | "enable_tracking": "Aktivera dessa för att hjälpa oss förbättra One-Click Miner", 88 | "report_issue": "Rapportera ett problem" 89 | }, 90 | "update": { 91 | "new_version_available": "Ny version tillgänglig", 92 | "download": "Ladda ner" 93 | } 94 | } -------------------------------------------------------------------------------- /frontend/src/i18n/tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "generic" : { 3 | "retry" : "Tekrar dene", 4 | "back_to_wallet" : "Cüzdana dön", 5 | "close" : "Kapat" 6 | }, 7 | "welcome" : { 8 | "alreadyrunning" : "Tek-tıkta-madencilik zaten çalışıyor. Birden fazla açamazsınız.", 9 | "click_button_to_start" : "Madenciliğe devam etmek için butona tıklayın.", 10 | "startmining" : "Madenciliğe Başla!", 11 | "makeapassword" : "Bir şifre oluşturun. Bu şifreyi unutmayın!", 12 | "password" : "Şifre", 13 | "confirmpassword" : "Şifreyi tekrar gir", 14 | "password_cannot_be_empty" : "Şifreyi boş bırakamazsınız", 15 | "password_mismatch" : "Şifreler eşleşmiyor", 16 | "error_initializing" : "Cüzdanı başlatırken bir şeyler ters gitti." 17 | }, 18 | "checks" : { 19 | "prerequisite" : "Gerekli bir program yükleniyor. İzin isteyen bir pencere açılabilir (ya da görev çubuğunuz renk değiştirebilir)", 20 | "checks_failed" : "Kontroller başarısız oldu.", 21 | "checking_mining_software" : "Madencilik yazılımı kontrol ediliyor...", 22 | "rapidfail" : "Ani oluşan hatalar kontrol ediliyor...", 23 | "compatibility" : "GPU uyumluluğu kontrol ediliyor...", 24 | "installing_miners" : "Madencilik yazılımı yükleniyor...", 25 | "verthash" : "Verthash dosyası onaylanıyor / oluşturuluyor..." 26 | }, 27 | "mining" : { 28 | "spendable_balance" : "Harcanabilir Bakiye", 29 | "still_maturing" : "Olgunlaşmakta olan:", 30 | "pending_pool_payout" : "havuz ödemesi bekleniyor", 31 | "pending_payout_info_unavailable" : "Beklenen ödeme bilgisi için lütfen bekleyin...", 32 | "waiting_for_miners" : "Madencinin başlaması bekleniyor", 33 | "expected_earnings_24h" : "Tahmini Kazanç (24 saat)", 34 | "estimating" : "Tahmin ediliyor...", 35 | "stop_mining" : "Madenciliği durdur", 36 | "active_pool" : "Aktif havuz", 37 | "pool_fee" : "Havuz komisyonu", 38 | "copy_address" : "Cüzdan adresinizi kopyalayın" 39 | }, 40 | "sending" : { 41 | "send_all_to" : "Kazdığım tüm coinleri şu adrese gönder:", 42 | "youre_sending_x_to" : "Bu adrese {receivedBalance} adet coin yolluyorsunuz;", 43 | "youre_sending_x_in_y_txs_to" : "Bu adrese {receivedBalance} adet coini {receivedTxCount} adet transferle yolluyorsunuz ", 44 | "receiver_address" : "Alıcı Adresi", 45 | "wallet_password" : "Cüzdan Şifresi", 46 | "send" : "Gönder", 47 | "coins_sent" : "Coinleriniz gönderildi!", 48 | "view_trans_plural" : "Transferleri görüntüle", 49 | "view_trans_singular" : "Transferi görüntüle", 50 | "failed_to_send" : "Coinleriniz gönderilemedi", 51 | "password_required" : "Cüzdan şifresini giriniz.", 52 | "invalid_address" : "Geçersiz adres.", 53 | "script_failure" : "Script başarısız oldu.", 54 | "could_not_calculate_fee" : "Komisyon hesaplanamadı", 55 | "insufficient_funds" : "Yetersiz bakiye", 56 | "sign_failed" : "Transfer imzalanamadı. Şifreyi kontrol ediniz.", 57 | "send_failed" : "Transfer yapılamadı. Daha fazla bilgi için debug.log inceleyiniz." 58 | }, 59 | "settings" : { 60 | "enable_debug" : "Debugging'i aktif et", 61 | "enable_debug_sub" : "Madenci konsolunun çıktılarını debug.log'a dahil et. Log boyutu çok büyüyebilir", 62 | "auto_start" : "Otomatik başlat", 63 | "auto_start_sub" : "Madenciyi bilgisayar açıldığında otomatik başlat", 64 | "closed_source" : "Kapalı kaynak kodlu madenci yazılımlarını kullan", 65 | "closed_source_sub" : "Daha iyi hashrate alırsınız, fakat yazılımın geliştiricisi sizden komisyon alır", 66 | "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.", 67 | "testnet":"Testnet modu", 68 | "testnet_sub":"Testnet modunu aktifleştirir. Kazılan coinler gerçek değildir.", 69 | "skipverthashverify":"Başlangıçta Verthash'i doğrulama", 70 | "skipverthashverify_sub":"Başlangıçta Verthash dosyasının bütünlüğünü doğrulamayı atlayacak (önerilmez)", 71 | "save_n_restart" : "Kaydet ve Yeniden Başlat", 72 | "pool": "Madenci havuzu" 73 | }, 74 | "tabbar" : { 75 | "wallet" : "Cüzdan", 76 | "send_coins" : "Coin gönder (DOGE)", 77 | "settings" : "Ayarlar" 78 | }, 79 | "tracking": { 80 | "update_available" : "Yeni bir güncelleme var.", 81 | "tracking_enabled" : "Anonim olarak kullanıcı istatistiği gönderiyorsunuz", 82 | "disable_tracking" : "Deaktive et", 83 | "tracking_disabled" : "Kullanım istatistiği yollamıyorsunuz", 84 | "enable_tracking" : "Kullanıcı deneyimini geliştirmemize yardımcı olmak için aktif edin", 85 | "report_issue" : "Sorun bildir" 86 | }, 87 | "update" : { 88 | "new_version_available" : "Yeni bir versiyon mevcut", 89 | "download" : "İndir" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /miners/lyclminer.go: -------------------------------------------------------------------------------- 1 | package miners 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "strconv" 9 | "strings" 10 | "sync" 11 | 12 | "github.com/vertiond/verthash-one-click-miner/logging" 13 | "github.com/vertiond/verthash-one-click-miner/util" 14 | ) 15 | 16 | // Compile time assertion on interface 17 | var _ MinerImpl = &LyclMinerImpl{} 18 | 19 | type LyclMinerImpl struct { 20 | binaryRunner *BinaryRunner 21 | hashRates map[int64]uint64 22 | hashRatesLock sync.Mutex 23 | } 24 | 25 | func NewLyclMinerImpl(br *BinaryRunner) MinerImpl { 26 | return &LyclMinerImpl{binaryRunner: br, hashRates: map[int64]uint64{}, hashRatesLock: sync.Mutex{}} 27 | } 28 | 29 | func (l *LyclMinerImpl) Configure(args BinaryArguments) error { 30 | os.Remove(filepath.Join(util.DataDirectory(), "lyclMiner_tmpl.conf")) 31 | err := l.binaryRunner.launch([]string{"-g", filepath.Join(util.DataDirectory(), "lyclMiner_tmpl.conf")}, false) 32 | err2 := l.binaryRunner.cmd.Wait() 33 | if err != nil { 34 | return err 35 | } 36 | if err2 != nil { 37 | return err2 38 | } 39 | 40 | if !l.binaryRunner.cmd.ProcessState.Success() { 41 | return fmt.Errorf("Was unable to configure lyclMiner. Exit code %d", l.binaryRunner.cmd.ProcessState.ExitCode()) 42 | } 43 | 44 | in, err := os.Open(filepath.Join(util.DataDirectory(), "lyclMiner_tmpl.conf")) 45 | if err != nil { 46 | logging.Error(err) 47 | return err 48 | } 49 | defer in.Close() 50 | 51 | os.Remove(filepath.Join(util.DataDirectory(), "lyclMiner.conf")) 52 | out, err := os.Create(filepath.Join(util.DataDirectory(), "lyclMiner.conf")) 53 | if err != nil { 54 | logging.Error(err) 55 | return err 56 | } 57 | defer func() { 58 | err := out.Close() 59 | if err != nil { 60 | logging.Error(err) 61 | } 62 | }() 63 | 64 | scanner := bufio.NewScanner(in) 65 | skip := false 66 | for scanner.Scan() { 67 | line := scanner.Text() 68 | if strings.HasPrefix(line, "#") { 69 | skip = false 70 | } 71 | if strings.HasPrefix(line, "\n\n", args.StratumUrl, args.StratumUsername, args.StratumPassword)) 73 | if err != nil { 74 | return err 75 | } 76 | skip = true 77 | } 78 | if !skip { 79 | _, err = out.WriteString(fmt.Sprintf("%s\n", line)) 80 | if err != nil { 81 | return err 82 | } 83 | } 84 | } 85 | if err := scanner.Err(); err != nil { 86 | return err 87 | } 88 | return nil 89 | } 90 | 91 | func (l *LyclMinerImpl) ParseOutput(line string) { 92 | if l.binaryRunner.Debug { 93 | logging.Debugf("[lyclMiner] %s\n", line) 94 | } 95 | line = strings.TrimSpace(line) 96 | if strings.Contains(line, "Device #") && strings.HasSuffix(line, "H/s") { 97 | startDeviceIdx := strings.Index(line, "Device #") 98 | endDeviceIdx := strings.Index(line[startDeviceIdx:], ":") 99 | deviceIdxString := line[startDeviceIdx+8 : startDeviceIdx+endDeviceIdx] 100 | deviceIdx, err := strconv.ParseInt(deviceIdxString, 10, 64) 101 | if err != nil { 102 | return 103 | } 104 | 105 | startMHs := strings.LastIndex(line, ", ") 106 | if startMHs > -1 { 107 | hashRateUnit := strings.ToUpper(line[len(line)-4 : len(line)-3]) 108 | line = line[startMHs+2 : len(line)-5] 109 | f, err := strconv.ParseFloat(line, 64) 110 | if err != nil { 111 | logging.Errorf("Error parsing hashrate: %s\n", err.Error()) 112 | } 113 | if hashRateUnit == "K" { 114 | f = f * 1000 115 | } else if hashRateUnit == "M" { 116 | f = f * 1000 * 1000 117 | } else if hashRateUnit == "G" { 118 | f = f * 1000 * 1000 * 1000 119 | } 120 | l.hashRatesLock.Lock() 121 | l.hashRates[deviceIdx] = uint64(f) 122 | l.hashRatesLock.Unlock() 123 | } 124 | } 125 | } 126 | 127 | func (l *LyclMinerImpl) HashRate() uint64 { 128 | totalHash := uint64(0) 129 | l.hashRatesLock.Lock() 130 | for _, h := range l.hashRates { 131 | totalHash += h 132 | } 133 | l.hashRatesLock.Unlock() 134 | return totalHash 135 | } 136 | 137 | func (l *LyclMinerImpl) ConstructCommandlineArgs(args BinaryArguments) []string { 138 | return []string{filepath.Join(util.DataDirectory(), "lyclMiner.conf")} 139 | } 140 | 141 | func (l *LyclMinerImpl) AvailableGPUs() int8 { 142 | tmpCfg := filepath.Join(util.DataDirectory(), "lyclMiner_tmp.conf") 143 | err := l.binaryRunner.launch([]string{"-g", tmpCfg}, false) 144 | err2 := l.binaryRunner.cmd.Wait() 145 | if err != nil { 146 | return 0 147 | } 148 | if err2 != nil { 149 | return 0 150 | } 151 | 152 | if !l.binaryRunner.cmd.ProcessState.Success() { 153 | return 0 154 | } 155 | 156 | in, err := os.Open(filepath.Join(util.DataDirectory(), "lyclMiner_tmpl.conf")) 157 | if err != nil { 158 | logging.Error(err) 159 | return 0 160 | } 161 | gpu := int8(0) 162 | scanner := bufio.NewScanner(in) 163 | for scanner.Scan() { 164 | line := scanner.Text() 165 | if strings.HasPrefix(line, "