├── .gitignore
├── version.py
├── README.md
├── docker-build.sh
├── docker-run.sh
├── docker-dev-build.sh
├── morpavsolver
├── morpavsolver.so
└── __init__.py
├── res
├── gui_rc.qrc
└── miner.svg
├── docker-dev-run.sh
├── run.sh
├── Dockerfile-dev
├── Dockerfile
├── LICENSE
├── gui.py
├── tests.py
└── epycyzm.py
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 |
3 |
--------------------------------------------------------------------------------
/version.py:
--------------------------------------------------------------------------------
1 | VERSION = 'slush/epycyzm/16.10'
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # epycyzm
2 | Experimental Python CPU (yet) Zcash miner
3 |
--------------------------------------------------------------------------------
/docker-build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | docker build -t epycyzm .
4 |
5 |
--------------------------------------------------------------------------------
/docker-run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | docker run --name epycyzm --rm -ti epycyzm "$@"
4 |
5 |
--------------------------------------------------------------------------------
/docker-dev-build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | docker build -f Dockerfile-dev -t epycyzm-dev .
4 |
5 |
--------------------------------------------------------------------------------
/morpavsolver/morpavsolver.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slush0/epycyzm/HEAD/morpavsolver/morpavsolver.so
--------------------------------------------------------------------------------
/res/gui_rc.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | miner.svg
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docker-dev-run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | docker run --name epycyzm-dev --rm -ti -v `pwd`:/epycyzm epycyzm-dev "$@"
4 |
5 |
--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Example usage. You can try it for a hour as a donation :-)
4 | nice -n9 ./epycyzm.py "$@"
5 |
--------------------------------------------------------------------------------
/Dockerfile-dev:
--------------------------------------------------------------------------------
1 | FROM python:3.5
2 |
3 | MAINTAINER Ondrej Sika
4 |
5 | RUN pip install cffi
6 |
7 | WORKDIR /epycyzm
8 |
9 | ENTRYPOINT ["./run.sh"]
10 | CMD ["stratum+tcp://slush:x@zec.slushpool.com:4444"]
11 |
12 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.5
2 |
3 | MAINTAINER Ondrej Sika
4 |
5 | RUN apt-get update
6 | RUN apt-get install -y git
7 | RUN git clone https://github.com/slush0/epycyzm.git
8 | RUN pip install cffi
9 |
10 | WORKDIR /epycyzm
11 | ENTRYPOINT ["./run.sh"]
12 | CMD ["stratum+tcp://slush:x@zec.slushpool.com:4444"]
13 |
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 slush0
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/morpavsolver/__init__.py:
--------------------------------------------------------------------------------
1 | # https://github.com/morpav/zceq_solver--bin
2 |
3 | from cffi import FFI
4 | import os.path
5 | import inspect
6 |
7 | ffi = None
8 | library = None
9 |
10 | library_header = """
11 | typedef struct {
12 | char data[1344];
13 | } Solution;
14 |
15 | typedef struct {
16 | unsigned int data[512];
17 | } ExpandedSolution;
18 |
19 | typedef struct HeaderAndNonce {
20 | char data[140];
21 | } HeaderAndNonce;
22 |
23 | typedef struct ZcEquihashSolverT ZcEquihashSolver;
24 |
25 | ZcEquihashSolver* CreateSolver(void);
26 |
27 | void DestroySolver(ZcEquihashSolver* solver);
28 |
29 | int FindSolutions(ZcEquihashSolver* solver, HeaderAndNonce* inputs,
30 | Solution solutions[], int max_solutions);
31 |
32 | int ValidateSolution(ZcEquihashSolver* solver, HeaderAndNonce* inputs, Solution* solutions);
33 |
34 | void RunBenchmark(long long nonce_start, int iterations);
35 |
36 | bool ExpandedToMinimal(Solution* minimal, ExpandedSolution* expanded);
37 |
38 | bool MinimalToExpanded(ExpandedSolution* expanded, Solution* minimal);
39 |
40 | """
41 |
42 | def load_library(path=None):
43 | global library, ffi
44 | assert library is None
45 |
46 | ffi = FFI()
47 | ffi.cdef(library_header)
48 |
49 | if path is None:
50 | path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'morpavsolver.so')
51 | library = ffi.dlopen(path)
52 | assert library is not None
53 |
54 |
55 | class Solver:
56 | def __init__(self):
57 | self.solver_ = self.header_ = self.solutions_ = self.solution_to_check_ = None
58 | self._ensure_library()
59 | assert library and ffi
60 | self.solver_ = library.CreateSolver()
61 | self.header_ = ffi.new("HeaderAndNonce*")
62 | self.solutions_ = ffi.new("Solution[16]")
63 | self.solution_to_check_ = ffi.new("Solution*")
64 | self.expanded_tmp_ = ffi.new("ExpandedSolution*")
65 |
66 | def __del__(self):
67 | # Free the underlying resources on destruction
68 | library.DestroySolver(self.solver_);
69 | self.solver_ = None
70 | # cffi's cdata are collected automatically
71 | self.header_ = self.solutions_ = self.solution_to_check_ = None
72 |
73 | def _ensure_library(self):
74 | # Try to load library from standard
75 | if (library is None):
76 | load_library()
77 |
78 | def run_benchmark(self, iterations=10, nonce_start=0):
79 | library.RunBenchmark(nonce_start, iterations)
80 |
81 | def find_solutions(self, block_header):
82 | assert len(block_header) == 140
83 | self.header_.data = block_header
84 | return library.FindSolutions(self.solver_, self.header_, self.solutions_, 16);
85 |
86 | def get_solution(self, num):
87 | assert(num >= 0 and num < 16)
88 | return bytes(ffi.buffer(self.solutions_[num].data))
89 |
90 | def validate_solution(self, block_header, solution):
91 | assert len(block_header) == 140
92 | assert len(solution) == 1344
93 | self.solution_to_check_.data = solution
94 | return library.ValidateSolution(self.solver_, self.header_, self.solution_to_check_);
95 |
96 | def list_to_minimal(self, expanded):
97 | if isinstance(expanded, (list, tuple)):
98 | assert len(expanded) == 512
99 | minimal = ffi.new("Solution*")
100 | tmp = self.expanded_tmp_
101 | for i, idx in enumerate(expanded):
102 | tmp.data[i] = idx
103 | expanded = tmp
104 |
105 | res = library.ExpandedToMinimal(minimal, expanded)
106 | assert res
107 | return minimal
108 |
109 | def minimal_to_list(self, minimal):
110 | tmp = self.expanded_tmp_
111 | res = library.MinimalToExpanded(tmp, minimal)
112 | assert res
113 | result = [tmp.data[i] for i in range(512)]
114 | return result
115 |
116 | __all__ = ['Solver', 'load_library']
117 |
--------------------------------------------------------------------------------
/res/miner.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
94 |
--------------------------------------------------------------------------------
/gui.py:
--------------------------------------------------------------------------------
1 | from PySide import QtCore, QtGui
2 |
3 | import gui_rc
4 |
5 | class Window(QtGui.QDialog):
6 | def __init__(self):
7 | super(Window, self).__init__()
8 |
9 | self.createIconGroupBox()
10 | self.createMessageGroupBox()
11 |
12 | self.iconLabel.setMinimumWidth(self.durationLabel.sizeHint().width())
13 |
14 | self.createActions()
15 | self.createTrayIcon()
16 |
17 | self.showMessageButton.clicked.connect(self.showMessage)
18 | self.showIconCheckBox.toggled.connect(self.trayIcon.setVisible)
19 | self.iconComboBox.currentIndexChanged[int].connect(self.setIcon)
20 | self.trayIcon.messageClicked.connect(self.messageClicked)
21 | self.trayIcon.activated.connect(self.iconActivated)
22 |
23 | mainLayout = QtGui.QVBoxLayout()
24 | mainLayout.addWidget(self.iconGroupBox)
25 | mainLayout.addWidget(self.messageGroupBox)
26 | self.setLayout(mainLayout)
27 |
28 | self.iconComboBox.setCurrentIndex(1)
29 | self.trayIcon.show()
30 |
31 | self.setWindowTitle("Systray")
32 | self.resize(400, 300)
33 |
34 | def setVisible(self, visible):
35 | self.minimizeAction.setEnabled(visible)
36 | self.maximizeAction.setEnabled(not self.isMaximized())
37 | self.restoreAction.setEnabled(self.isMaximized() or not visible)
38 | super(Window, self).setVisible(visible)
39 |
40 | def closeEvent(self, event):
41 | if self.trayIcon.isVisible():
42 | QtGui.QMessageBox.information(self, "Systray",
43 | "The program will keep running in the system tray. To "
44 | "terminate the program, choose Quit in the "
45 | "context menu of the system tray entry.")
46 | self.hide()
47 | event.ignore()
48 |
49 | def setIcon(self, index):
50 | icon = self.iconComboBox.itemIcon(index)
51 | self.trayIcon.setIcon(icon)
52 | self.setWindowIcon(icon)
53 |
54 | self.trayIcon.setToolTip(self.iconComboBox.itemText(index))
55 |
56 | def iconActivated(self, reason):
57 | if reason in (QtGui.QSystemTrayIcon.Trigger, QtGui.QSystemTrayIcon.DoubleClick):
58 | self.iconComboBox.setCurrentIndex(
59 | (self.iconComboBox.currentIndex() + 1)
60 | % self.iconComboBox.count())
61 | elif reason == QtGui.QSystemTrayIcon.MiddleClick:
62 | self.showMessage()
63 |
64 | def showMessage(self):
65 | icon = QtGui.QSystemTrayIcon.MessageIcon(
66 | self.typeComboBox.itemData(self.typeComboBox.currentIndex()))
67 | self.trayIcon.showMessage(self.titleEdit.text(),
68 | self.bodyEdit.toPlainText(), icon,
69 | self.durationSpinBox.value() * 1000)
70 |
71 | def messageClicked(self):
72 | QtGui.QMessageBox.information(None, "Systray",
73 | "Sorry, I already gave what help I could.\nMaybe you should "
74 | "try asking a human?")
75 |
76 | def createIconGroupBox(self):
77 | self.iconGroupBox = QtGui.QGroupBox("Tray Icon")
78 |
79 | self.iconLabel = QtGui.QLabel("Icon:")
80 |
81 | self.iconComboBox = QtGui.QComboBox()
82 | self.iconComboBox.addItem(QtGui.QIcon(':/icons/miner.svg'), "Miner")
83 | self.iconComboBox.addItem(QtGui.QIcon(':/images/heart.svg'), "Heart")
84 | self.iconComboBox.addItem(QtGui.QIcon(':/images/trash.svg'), "Trash")
85 |
86 | self.showIconCheckBox = QtGui.QCheckBox("Show icon")
87 | self.showIconCheckBox.setChecked(True)
88 |
89 | iconLayout = QtGui.QHBoxLayout()
90 | iconLayout.addWidget(self.iconLabel)
91 | iconLayout.addWidget(self.iconComboBox)
92 | iconLayout.addStretch()
93 | iconLayout.addWidget(self.showIconCheckBox)
94 | self.iconGroupBox.setLayout(iconLayout)
95 |
96 | def createMessageGroupBox(self):
97 | self.messageGroupBox = QtGui.QGroupBox("Balloon Message")
98 |
99 | typeLabel = QtGui.QLabel("Type:")
100 |
101 | self.typeComboBox = QtGui.QComboBox()
102 | self.typeComboBox.addItem("None", QtGui.QSystemTrayIcon.NoIcon)
103 | self.typeComboBox.addItem(self.style().standardIcon(
104 | QtGui.QStyle.SP_MessageBoxInformation), "Information",
105 | QtGui.QSystemTrayIcon.Information)
106 | self.typeComboBox.addItem(self.style().standardIcon(
107 | QtGui.QStyle.SP_MessageBoxWarning), "Warning",
108 | QtGui.QSystemTrayIcon.Warning)
109 | self.typeComboBox.addItem(self.style().standardIcon(
110 | QtGui.QStyle.SP_MessageBoxCritical), "Critical",
111 | QtGui.QSystemTrayIcon.Critical)
112 | self.typeComboBox.setCurrentIndex(1)
113 |
114 | self.durationLabel = QtGui.QLabel("Duration:")
115 |
116 | self.durationSpinBox = QtGui.QSpinBox()
117 | self.durationSpinBox.setRange(5, 60)
118 | self.durationSpinBox.setSuffix(" s")
119 | self.durationSpinBox.setValue(15)
120 |
121 | durationWarningLabel = QtGui.QLabel("(some systems might ignore this "
122 | "hint)")
123 | durationWarningLabel.setIndent(10)
124 |
125 | titleLabel = QtGui.QLabel("Title:")
126 |
127 | self.titleEdit = QtGui.QLineEdit("Cannot connect to network")
128 |
129 | bodyLabel = QtGui.QLabel("Body:")
130 |
131 | self.bodyEdit = QtGui.QTextEdit()
132 | self.bodyEdit.setPlainText("Don't believe me. Honestly, I don't have "
133 | "a clue.\nClick this balloon for details.")
134 |
135 | self.showMessageButton = QtGui.QPushButton("Show Message")
136 | self.showMessageButton.setDefault(True)
137 |
138 | messageLayout = QtGui.QGridLayout()
139 | messageLayout.addWidget(typeLabel, 0, 0)
140 | messageLayout.addWidget(self.typeComboBox, 0, 1, 1, 2)
141 | messageLayout.addWidget(self.durationLabel, 1, 0)
142 | messageLayout.addWidget(self.durationSpinBox, 1, 1)
143 | messageLayout.addWidget(durationWarningLabel, 1, 2, 1, 3)
144 | messageLayout.addWidget(titleLabel, 2, 0)
145 | messageLayout.addWidget(self.titleEdit, 2, 1, 1, 4)
146 | messageLayout.addWidget(bodyLabel, 3, 0)
147 | messageLayout.addWidget(self.bodyEdit, 3, 1, 2, 4)
148 | messageLayout.addWidget(self.showMessageButton, 5, 4)
149 | messageLayout.setColumnStretch(3, 1)
150 | messageLayout.setRowStretch(4, 1)
151 | self.messageGroupBox.setLayout(messageLayout)
152 |
153 | def createActions(self):
154 | self.minimizeAction = QtGui.QAction("Mi&nimize", self,
155 | triggered=self.hide)
156 |
157 | self.maximizeAction = QtGui.QAction("Ma&ximize", self,
158 | triggered=self.showMaximized)
159 |
160 | self.restoreAction = QtGui.QAction("&Restore", self,
161 | triggered=self.showNormal)
162 |
163 | self.quitAction = QtGui.QAction("&Quit", self,
164 | triggered=QtGui.qApp.quit)
165 |
166 | def createTrayIcon(self):
167 | self.trayIconMenu = QtGui.QMenu(self)
168 | self.trayIconMenu.addAction(self.minimizeAction)
169 | self.trayIconMenu.addAction(self.maximizeAction)
170 | self.trayIconMenu.addAction(self.restoreAction)
171 | self.trayIconMenu.addSeparator()
172 | self.trayIconMenu.addAction(self.quitAction)
173 |
174 | self.trayIcon = QtGui.QSystemTrayIcon(self)
175 | self.trayIcon.setContextMenu(self.trayIconMenu)
176 |
177 |
178 | if __name__ == '__main__':
179 |
180 | import sys
181 |
182 | app = QtGui.QApplication(sys.argv)
183 |
184 | if not QtGui.QSystemTrayIcon.isSystemTrayAvailable():
185 | QtGui.QMessageBox.critical(None, "Systray",
186 | "I couldn't detect any system tray on this system.")
187 | sys.exit(1)
188 |
189 | QtGui.QApplication.setQuitOnLastWindowClosed(False)
190 |
191 | window = Window()
192 | window.show()
193 | sys.exit(app.exec_())
194 |
--------------------------------------------------------------------------------
/tests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from pyzceqsolver import Solver
4 | import binascii
5 | from hashlib import sha256
6 |
7 | def main():
8 | vectors = (
9 | {
10 | 'height': 100, # testnet rc4
11 | 'hash': b'004a4bcb42c9fe51332ea01c9bf9eeff7f9bd6957711862c5471041fd94ef95b',
12 | 'block': '04000000ad6c8c34c3d72887c5eab6276564c349d60df2863495ff22f4079488813bf0018e20f8505a31f69381c0fbeabe3f24b99b6109e0e66eaea14cf9bb1ac2e3f88c0000000000000000000000000000000000000000000000000000000000000000bb341258a81802200600dfe244233d83bec8985f6c5b5e5e248d9b19204451df5dc2f4b7f87c0000fd400500b31c3ae78b552cfbd1a6df3ef175e610c63980aa3fdeee24be6727b5d40645c91570118e5faf1c0a0611572ee7f0c8246398bc62efce1ff899c14efc93dd57804abd80da0ac2e71617b00bfb29fee324fda66805ffbe283b8e6ef3b4fd43ba66309a95cb01d2e572135406763cc4d573738741859975bf756b739340120d15d8cbaed4b6790e2e01b5d3e8cd09e1d8f77bb73d1be7ab4021fec317ea26ce9b570f61d7749fc9e208cebfba2ced40e3762d927b412f6612733adf35bc347cf619dc20ab5df05497f85bc3ecaba113be5b9a292b74d9938c3e08a972b353f6eba4c950d959a0024b846d8642d466e7045049b0b95d7d1b77745fd90f0c6a664ecc8c202bf7b590e2c0cacc4d1a307d652f314617fc4e954bfd32fb54f891f0fee5e38af8e1b4238112016e5bf3f5ba21b4567dfc431146686ed13d26400adf810a34f6eaeea3b196ae45354f408bac700113fd95f6cb0b81ea6d128023d968a987129ed11a18442d4aa20955f7cb8ae2bba777a7f57049789cbf262ddc0d539a3da7ecb46575e765ada657a85eb94744b202c0d52cd335a19f845c1f4c08e54d50f52502061edfcac89d5df7c43383783aa903c24f94ba400726d03250d3d8ffc52986b2df3d5a5ba4ff3e7ce0510a9d7bc6384f101f3f072238fcae7b00b45dbf31250b91e0de30d6ab7367a0c8a56ef9909251145cdd1f01418921b2913080a14f909292f6d3d59be5717d7209b853ffbb48c0cd233db173ef770ace1f64f5422c033495709d54f3335d22b0872813261d4d40970b981a40bdb93b3525abb4f3224fcfe994121d041e39d6021564b9fc111fbe9def83311ea56c28d13d4a48ca1509bbe745e750b58fc0f25832dd091d29598d3e59123f357df04c20813414618b5eeaef9d004dcd6f9a1f238f8fe9651a3ba21ad6a723d9c9b5c6186f4ad80105ae08495f391b196c219e0194eff2a6bbf83a3a2123434e499b8f4d6230735634d5410544dd70e3f80571df6fb199d5637640d6dc637c4b91fd929abe5a229ee47fc0d7e493baef99280e68656aee6ff9cb64097f6b313885b5f7cfcfad4e8afeec1fa7355df5b60b26da0b2f4b464ba9c616ae3f3ccf9208a7178ae01c0d0c985ba6313f77dc448963a6d6c2f7e57fb4f125e389d66699467564468aebe4fcd0f2d0b0fedd2a013dbec635892ca584f612b404aac832408d388c55051928bd38211009a1b9b2ed6b3fbc3da21ed515411710b8f5d0537c8f5d70a3265f685c16a1ac36485924a16bded44b7a948e20026c77cb1c6e0bad9739da0424b2c49b958da3f2ef357c2e403c4d9ed496f1dc1fb785394c4e9493881f5281b9e798a2e73d7dff4504994b8e3e8a331f0c55d1ca0e955a2cb933da91d3435d7ff68d255f957b08663abf7dc27a2380d8ee5f024e8ccb8d1952d8f13a71e063a128ae743cb5f7180e700e9e25615a2fc293f2b148c7cb419f21fe203311db1de3cddbf3fbbc04a2286073a8ce36199409c4355aee2ed4e8e9096991b6fc2dfd302e706194477620be22f410d2c7d5c09786e034da708f00e8fe12c1335521ea9f21b13ffcc3e41c3f370de9aff0ba2f94248843d7f662fe776b984607b5b626ca05c8de5bd7258b8befc31b406b1b6293cdbdd663ce0fdbdd33a80d2307eb30231ad58a4c4364082b6ef65573bbc2f232bba1d24b16d9c96fc143610927f3e1457b99edf10ee592ed028456fff761016f18411ffb65af9c5e7d1ae88e84c8bd614df64c6275ddc46f19c4c3fac8d515b4b9e114237f15cbebf22be62e0ef91c976b021317004a29c3e6210dbb37d1842153840dafb777dd93285d09a8f38e6b5de0b06294ba19c6f94ba3f9335431f73dc3cd9126d1f417f597663d05bdc9f93034ae0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0401640103ffffffff02404b4c0000000000232103bc707cd02a0bcc498afceb1748822482ef8e179d79d8dd672913f61c16484069acd01213000000000017a914ef775f1f997f122a062fff1a2d7443abd1f9c6428700000000',
13 | 'sol_id': 0
14 | },
15 | {
16 | 'height': 1000, # testnet rc4
17 | 'hash': b'00000012d0323e496c5c2ded0fc47c555894d29a1e092fb3af2df72e121078d9',
18 | 'block': '04000000708fcb43f55789d24439addb18cc94413e649e672e2099202514e58558000000150b1446c8ff33834e45152d7bbeb493f1f99c6d22e36968a6013e9d14e0d6c3000000000000000000000000000000000000000000000000000000000000000024101358ed7d011e5103000000000000010000000000000000000000000000000000000000000000fd4005001f028d4d071ba70f8229286178d9cfaee47f35654a259eb59464db29993977cff84bcdbe25de324c1213f7725beec79ba33588828f34ce5de8ada96fcf4668043b86c822783f960e98abbe66747e652735517a04b4e48058c4a887e8f0e090f6b6e434ef4bced9c42d28b9ebf4d6fef1179d130c283c8d29508aea939e0f6c4ae530e684ef5a4926787e5025eeed15d80d1a286eb4cca08bdc126c398610d65f7ab596f31b175f029f1c75a0da30e90f81d29ca1bf8d2b49751f41db1468bd4413f21c4b9b3df24f04ea72ad0a7ff8dc710768865127061d4c5d2b023d7d45dbf223913a66d8142554f49d2b5197c161528899f100c2d42bf99850059d28afecd2070df65606a64e75f979e2a3fe49d40935f57526d727a5a327c1048762892e26057d93372b4d7e0a8b34120beab0b4b0383be61e0bb31c0a504306567188b45785d17f96ce9d47ea42b2a63ef520004e4b8cbe049d3aa08de85206ce4eeef733199eec36b97dfa3c9723d8ba39b37e1c63e5a1bbeb2ed36f0d507b5307a7f54fdf61c2c812fc2440b25d735acd0e92cc98867a1455d56e711b5e9f95d8fbe9518a940265fd68b24a83fdcac041b0e52afb89912aee621b278464c7039b7ad5e4b12a93fb6a67b731161eea7707e3ab80c105fd82e23453a09a4fc5e64ff4fb568635c72c550c60b7732b1e751a19d2449d4e24192abb036057dd6940ef4b8e1e368458fcd6c9f82ed2c13116b13dd509990e57173d72625b5ddae0a4f3f7c4cf09bc4934de8cb165ccd68429ebe6e161ab13b6523432fc666857ef516583c393a81cb88dd268d33d8df505deb3ec0944174cb2c072e8a22c5dad61205d16ad13cac8c71d506a77e10fb264162c69870f9a1b9f8f0b763af0c24770eeca5412503c22b9b27db2f7d72b4b68345d4bd9a077becd76f2244ca63e69f39dc3ec00d12dc10554b5558fbdf03e0f385a49ea16162f857ee24e142c2485273177c83f13c952838aa55e0a4803c8a03e3d727e1d9e1e508dceaeaa52041b72e43a0595ea3e7b5b374f966fa2bc781df1671cf87d6c810b67d504b08dd2a2da977117277297c7163f9fc50e1cbf627c3f6ebf31f0be0203ede6e390cc3f5da85826ae0cbdd70f706eeedbe402b524ea766df3dd17d5525a1c4542ec3733915dd55964b3bd1a2c1b9bae8f01e8833ccac22fdb300533b569acd63eb59356e5f02669bb962419940334cde96f5cea833f3a105ee7790270f31522c61c99e6af43435d49225cf510bd1021104a1161b4cb539f7923c2ec7828909d9a528d3d890ba4b39c6fce151e773652e5381f81e116e377c4c72246a478a05551b3f1f5e24bf0bc49a4f55873808310acf716048a00f51e3e5348d8f8875db5ca6e50832a405bf5ee0e50870810d78c9f6055e331c77cdcad01c0cd7e3721a865caf1d5e16f7eab9dfa2ebfbb254bc32ff760762331c8219612a7b907ee752b975a890576f26cd7100e994a89f0968551f4e9bcd8f89d560f7b8ba9c3255d9738fe53f283ed8bc2f3e01991bb057ea7e8aad6351d38798512414a8f6dc9539f92050de436ab08087b2735c9819c917e8894955bccaf640dfe5f5bbd0ef60b1226a45c41f6070199479c3a09529f1360b23243d195e3565488fdd8a2eae8df0fda0479a1bab0203dedc1c31455857915c6723a55fa0c056270495a022f3479d8b31f4873119e5f75b6201c055d64ccb63bc021fa9f732384c7013e88921a847f0c2c6fa6986fc64dbae0955fe87be9a19282da6faa16d4ff39b60939b133d0f2d6e3dcad08bf24488e71599786a11656cc496643666e0f54b1675a30befc0817778a92964901428155b982e5d24402ef175f22343309dd4c1d9cd5c53da42aa3f67fc88f26191f598a0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0602e80302d700ffffffff0280f0fa0200000000232103ad63e41526f301370b3eac4f243efce6e1cc9f5907189360666f82312fe6d8f0ac20bcbe000000000017a914ef775f1f997f122a062fff1a2d7443abd1f9c6428700000000',
19 | 'sol_id': 2
20 | },
21 | {
22 | 'height': 70, # mainnet
23 | 'hash': b'0000c3e45b6183f5d1938d7293c0517ccbacbd53abd79eb8b8b5c9a06e50a66a',
24 | 'block': '04000000d88b45abc52c84d1345b6344b04a53abf92f05577b08c88d709fc110bb480200deb767c7c9cfdec27967faf5d0c09026da0b835b58a3acef9e39d7552db5e2a800000000000000000000000000000000000000000000000000000000000000001f7c135822b5021f1bb2b37300000000000000000000000000000000000000000000000000000002fd400500673cdd059b980150fe526e7fb7750e3e25b515f933ffaacb765a893f7652445bc073ad054b186fe718057ce0c584969247b2ca23609a5e87711b3c54d5fc2e021edb07b43e1df03fa2e557c47e55b9b139a445099751e11d48df60804cc57159fe23fd7bc6b932b0295719cf859aa6333054b98fda77e5f296f7fdb9161561ad757dc8489377d2f53c0c2e73de2c3234a7e7880014c7f42e87c9873daa964c7c92a2d2ab56afa004c3f58abfdfbbd524bcf17f6b76e5f6310b5e80bd0b7811ffc997d767eaa5c230225d35b5c612b1cd0d09a6bda04d82dff59bf8d1f99fa64acd735a94ac1a4cb63d3ff9e71feb3b1575e186ff461996b21c14190fe73d9f3c1d42b9fbf69cc179ed42b739257e17291c7c4f0afa0e922ff0d45766a26edb1e461c5b1b7212d913438462470d924b27ec23ed592b64acdd9d90137aedd39ebd3425f047d55597d72042af1e1fd330031bff47e6c30cbcef6cb034d9e5094e657fd8d9210901e7f697f0f251a9dfe3b64c7ac56a082c7176aa04eb47fbbc55a535aac61314f57ad5f1d3f77c1b7f42a2bc31b7e24f49184215a7a5757fe369f8db9cd203dfc8f714c2313dfd2c249d01f369ba3f507ce8dc0d1b35796b2d8bb7ae5314838c671819b45e30995a06727c7bdfe4fe65d2fb1135afe882e8e04b580701419cb6a09a516a8f6c8b14a67df037d55e4b70758605c1e46338939eb69d71132ab12ce489bae6b1f509283aae0a3f69590da10c02adb4e5d48d8735ec804c06eb76971f838485e222e1fd06a287114511da40210a2620e6505ca8ab0b2037c4526b793a897e7887aa0bac48e44261ce49cbebc3d69d55ad23210199619f45ecc6a8bda0365316ab99cfa77945632ff1ba645e23f56d3bacb17317c340e4b886f2036d476593c16c4cecc56d87d37bc73984e609805b12bdedd3722e1f0091e0d120ca9db30062d3c444dea97aaa8f5bc4981d2ea15785d0946ad7162270e7747e7195087416ff0641c164a457c57ef316653530c6e8eb22feb986611d554c7bbbee6c19c01069964556741efd8c5cbc8701ca23f86198cd6ccc1c923f6363fcd95aff5e395407d0c2630795e63986c17de72e7c72cf7bf95e755923789e18f1685059eeb57355dea7dd12cee439471c3637733bbe9e99b71d7e64e962e71c4e41beb5f2810175dd7f8feff25fc8da638e8b31f7717048929cd84bdadb13ee5b28f3f610b4cdafc9fbdd71e87858da07cb84dd7cdf3cb9e6d63767bdeffb3a795fffe17d18fe85e010cf741d3e2787369055040669c6d5befc061a5a337d09b78f556510aeecf2b821b4af35a8105ab79d0486732f25fef1b7035366f9ca8fd599ae3e065d3f42ec869321391b723bc51d701d7a7333cca5334813429f9d19c5dbae635fedcfaf093840d6bd2f0188e74cf68e4a52c96913039faa2b3df946fee04e0805987f089647058668c82e92442c5bbb8b3f9f050d5fda8b64e40e23899425f4cfed659de5055e6c023b389a37151962f5dc7473b61e29649dcc03b079240253e1660c41bcc5ca884210b829671596c438ce101049dfc6189fe97d01aa69084ffc47c6fb50ddf0a11f6406ab34e02dc17b2033955ef207e7aaee5dbb31490d2d0ef1ad184dce13980559ed5b3b5d573cf03309772cd3cd642363985860df34674ff0479575d24f1056df7a4a53c1339d0481c8e918a394ff0a1c8cd80a051746678f0e6ad1e5c18a5ba55e793c5e5ffae9231d6b5bf75429ad928a25296cd93b3e7455555ba60b1f6e50118aa6ba9433929d564e067e14f7bcad730d274a677f11a3e8f23172e9aea102829ea678f2560e9cd2d91c5da62bdf5ef138ffba584db72b2f0d04232f8df8c3115813e786052deb55f26dce457e9ff70101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03014600ffffffff02e0673500000000002321027a46eb513588b01b37ea24303f4b628afd12cc20df789fede0921e43cad3e875acf8590d000000000017a9147d46a730d31f97b1930d3368a967c309bd4d136a8700000000',
25 | 'sol_id': 1,
26 | }
27 | )
28 |
29 | s = Solver()
30 |
31 | for v in vectors:
32 | print("Testing block %s" % v['hash'])
33 | header = binascii.unhexlify(v['block'])[:140]
34 | solution = binascii.unhexlify(v['block'])[143:143+1344]
35 | sol_cnt = s.find_solutions(header)
36 |
37 | for i in range(sol_cnt):
38 | sol = s.get_solution(i)
39 | if i == v['sol_id']:
40 | if sol == solution:
41 | print("Computed solution PASSED")
42 | else:
43 | raise Exception("Solutions are different!")
44 | elif sol == solution:
45 | raise Exception("Something is seriously broken")
46 |
47 | # Recalculate block hash of given block
48 | tohash = binascii.unhexlify(v['block'])[:143+1344]
49 | print(binascii.hexlify(tohash))
50 | hash = sha256(sha256(tohash).digest()).digest()
51 | print("Block data fits block hash:", binascii.hexlify(hash[::-1]) == v['hash'])
52 |
53 | if __name__ == '__main__':
54 | main()
55 |
--------------------------------------------------------------------------------
/epycyzm.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | '''
3 | Experimental Python CPU (yet) Zcash miner
4 |
5 | Inspired by m0mchil's poclbm (thanks!), but heavily rewritten with asyncio.
6 |
7 | Miner icon:
8 | http://www.flaticon.com/free-icon/miner_206873
9 |
10 | (c) 2016 slush
11 | MIT license
12 | '''
13 |
14 | import re
15 | import os
16 | import json
17 | import time
18 | import struct
19 | import asyncio
20 | import binascii
21 | import itertools
22 | import traceback
23 | import threading
24 | import multiprocessing
25 | from hashlib import sha256
26 | from optparse import OptionGroup, OptionParser
27 | from concurrent.futures._base import TimeoutError
28 | from asyncio import coroutine, coroutines, futures
29 |
30 | try:
31 | import PySide
32 | gui_enabled = True
33 | except ImportError:
34 | gui_enabled = False
35 |
36 | from morpavsolver import Solver
37 | from version import VERSION
38 |
39 | class Server(object):
40 | @classmethod
41 | def from_url(cls, url):
42 | # Parses proto://user:password@zec.suprnova.cc:1234#tagname
43 | s = cls()
44 | x = re.match(r'^(.*\:\/\/)?((?P.*?)(\:(?P.*?))?\@)?(?P.*?)(\:(?P\d+))?(\#(?P.*?))?$', url)
45 | s.username = x.group('username') or ''
46 | s.password = x.group('password') or ''
47 | s.host = x.group('host')
48 | s.port = int(x.group('port') or s.port)
49 | s.tag = x.group('tag') or s.host # set host if tag not present
50 | return s
51 |
52 | def __repr__(self):
53 | return str(self.__dict__)
54 |
55 |
56 | class ServerSwitcher(object):
57 | def __init__(self, loop, servers, solvers):
58 | self.loop = loop
59 | self.servers = servers
60 | self.solvers = solvers
61 |
62 | @coroutine
63 | def run(self):
64 | for server in itertools.cycle(self.servers):
65 | try:
66 | client = StratumClient(self.loop, server, self.solvers)
67 | yield from client.connect()
68 | except KeyboardInterrupt:
69 | print("Closing...")
70 | self.solvers.stop()
71 | break
72 | except:
73 | traceback.print_exc()
74 |
75 | print("Server connection closed, trying again...")
76 | yield from asyncio.sleep(5)
77 |
78 | class StratumNotifier(object):
79 | def __init__(self, reader, on_notify):
80 | self.waiters = {}
81 | self.on_notify = on_notify
82 | self.reader = reader
83 | self.task = None
84 |
85 | def run(self):
86 | # self.task = asyncio.ensure_future(self.observe())
87 | self.task = asyncio.async(self.observe())
88 | return self.task
89 |
90 | @coroutine
91 | def observe(self):
92 | try:
93 | while True:
94 | data = yield from self.reader.readline()
95 | if data == b'':
96 | raise Exception("Server closed connection.")
97 |
98 | try:
99 | msg = json.loads(data.decode())
100 | except:
101 | raise Exception("Recieved corrupted data from server: %s" % data)
102 |
103 | if msg['id'] == None:
104 | # It is notification
105 | yield from self.on_notify(msg)
106 | else:
107 | # It is response of our call
108 | self.waiters[int(msg['id'])].set_result(msg)
109 |
110 | except Exception as e:
111 | # Do not try to recover from errors, let ServerSwitcher handle this
112 | traceback.print_exc()
113 | raise
114 |
115 | @coroutine
116 | def wait_for(self, msg_id):
117 | f = asyncio.Future()
118 | self.waiters[msg_id] = f
119 | return (yield from asyncio.wait_for(f, 10))
120 |
121 | class Job(object):
122 | @classmethod
123 | def from_params(cls, params):
124 | j = cls()
125 | j.job_id = params[0]
126 | j.version = binascii.unhexlify(params[1])
127 | j.prev_hash = binascii.unhexlify(params[2])
128 | j.merkle_root = binascii.unhexlify(params[3])
129 | j.reserved = binascii.unhexlify(params[4])
130 | j.ntime = binascii.unhexlify(params[5])
131 | j.nbits = binascii.unhexlify(params[6])
132 | j.clean_job = bool(params[7])
133 |
134 | assert (len(j.version) == 4)
135 | assert (len(j.prev_hash) == 32)
136 | assert (len(j.merkle_root) == 32)
137 | assert (len(j.reserved) == 32)
138 | assert (len(j.ntime) == 4)
139 | assert (len(j.nbits) == 4)
140 |
141 | return j
142 |
143 | def set_target(self, target):
144 | self.target = target
145 |
146 | def build_header(self, nonce):
147 | assert(len(nonce) == 32)
148 |
149 | header = self.version + self.prev_hash + self.merkle_root + self.reserved + self.ntime + self.nbits + nonce
150 | assert(len(header) == 140)
151 | return header
152 |
153 | @classmethod
154 | def is_valid(cls, header, solution, target):
155 | assert (len(header) == 140)
156 | assert (len(solution) == 1344 + 3)
157 |
158 | hash = sha256(sha256(header + solution).digest()).digest()
159 | print("hash %064x" % int.from_bytes(hash, 'little'))
160 |
161 | return int.from_bytes(hash, 'little') < target
162 |
163 | def __repr__(self):
164 | return str(self.__dict__)
165 |
166 | class CpuSolver(threading.Thread):
167 | def __init__(self, loop, counter):
168 | super(CpuSolver, self).__init__()
169 | self._stop = False
170 | self.loop = loop
171 | self.counter = counter
172 |
173 | self.job = None
174 | self.nonce1 = None
175 | self.nonce2_int = 0
176 | self.on_share = None
177 |
178 | def stop(self):
179 | raise Exception("FIXME")
180 | self._stop = True
181 |
182 | def set_nonce(self, nonce1):
183 | self.nonce1 = nonce1
184 |
185 | def new_job(self, job, solver_nonce, on_share):
186 | self.job = job
187 | self.solver_nonce = solver_nonce
188 | self.on_share = on_share
189 |
190 | def increase_nonce(self):
191 | if self.nonce2_int > 2**62:
192 | self.nonce2_int = 0
193 |
194 | self.nonce2_int += 1
195 | return struct.pack('>q', self.nonce2_int)
196 |
197 | def run(self):
198 | print("Starting CPU solver")
199 | s = Solver()
200 |
201 | while self.job == None or self.nonce1 == None:
202 | time.sleep(2)
203 | print(".", end='', flush=True)
204 |
205 | while not self._stop:
206 | nonce2 = self.increase_nonce()
207 | nonce2 = nonce2.rjust(32 - len(self.nonce1) - len(self.solver_nonce), b'\0')
208 |
209 | header = self.job.build_header(self.nonce1 + self.solver_nonce + nonce2)
210 |
211 | sol_cnt = s.find_solutions(header)
212 | self.counter(sol_cnt) # Increase counter for stats
213 |
214 | for i in range(sol_cnt):
215 | solution = b'\xfd\x40\x05' + s.get_solution(i)
216 |
217 | if self.job.is_valid(header, solution, self.job.target):
218 | print("FOUND VALID SOLUTION!")
219 | # asyncio.run_coroutine_threadsafe(self.on_share(self.job, self.solver_nonce + nonce2, solution), self.loop)
220 | asyncio.async(self.on_share(self.job, self.solver_nonce + nonce2, solution), loop=self.loop)
221 |
222 | class SolverPool(object):
223 | def __init__(self, loop, gpus=0, cpus=0):
224 | self.solvers = []
225 | self.time_start = time.time()
226 | self.solutions = 0
227 |
228 | for i in range(cpus):
229 | s = CpuSolver(loop, self.inc_solutions)
230 | s.start()
231 | self.solvers.append(s)
232 |
233 | def inc_solutions(self, i):
234 | self.solutions += i
235 | print("%.02f H/s" % (self.solutions / (time.time() - self.time_start)))
236 |
237 | def set_nonce(self, nonce1):
238 | for i, s in enumerate(self.solvers):
239 | s.set_nonce(nonce1)
240 |
241 | def new_job(self, job, on_share):
242 | for i, s in enumerate(self.solvers):
243 | s.new_job(job,
244 | # Generate unique nonce1 for each solver
245 | struct.pack('>B', i),
246 | on_share)
247 |
248 | def stop(self):
249 | for s in self.solvers:
250 | s.stop()
251 |
252 | # Stratum protocol specification: https://github.com/zcash/zips/pull/78
253 | class StratumClient(object):
254 | def __init__(self, loop, server, solvers):
255 | self.loop = loop
256 | self.server = server
257 | self.solvers = solvers
258 | self.msg_id = 0 # counter of stratum messages
259 |
260 | self.writer = None
261 | self.notifier = None
262 |
263 | @coroutine
264 | def connect(self):
265 | print("Connecting to", self.server)
266 | asyncio.open_connection()
267 | reader, self.writer = yield from asyncio.open_connection(self.server.host, self.server.port, loop=self.loop)
268 |
269 | # Observe and route incoming message
270 | self.notifier = StratumNotifier(reader, self.on_notify)
271 | self.notifier.run()
272 |
273 | yield from self.subscribe()
274 | yield from self.authorize()
275 |
276 | while True:
277 | yield from asyncio.sleep(1)
278 |
279 | if self.notifier.task.done():
280 | # Notifier failed or wanted to stop procesing
281 | # Let ServerSwitcher catch this and round-robin connection
282 | raise self.notifier.task.exception() or Exception("StratumNotifier failed, restarting.")
283 |
284 | def new_id(self):
285 | self.msg_id += 1
286 | return self.msg_id
287 |
288 | def close(self):
289 | print('Close the socket')
290 | self.writer.close()
291 |
292 | @coroutine
293 | def on_notify(self, msg):
294 | if msg['method'] == 'mining.notify':
295 | print("Giving new job to solvers")
296 | j = Job.from_params(msg['params'])
297 | j.set_target(self.target)
298 | self.solvers.new_job(j, self.submit)
299 | return
300 |
301 | if msg['method'] == 'mining.set_target':
302 | print("Received set.target")
303 | self.target = int.from_bytes(binascii.unhexlify(msg['params'][0]), 'big')
304 | return
305 |
306 | print("Received unknown notification", msg)
307 |
308 | @coroutine
309 | def authorize(self):
310 | ret = yield from self.call('mining.authorize', self.server.username, self.server.password)
311 | if ret['result'] != True:
312 | raise Exception("Authorization failed: %s" % ret['error'])
313 | print("Successfully authorized as %s" % self.server.username)
314 |
315 | @coroutine
316 | def subscribe(self):
317 | ret = yield from self.call('mining.subscribe', VERSION, None, self.server.host, self.server.port)
318 | nonce1 = binascii.unhexlify(ret['result'][1])
319 | print("Successfully subscribed for jobs")
320 | self.solvers.set_nonce(nonce1)
321 | return nonce1
322 |
323 | @coroutine
324 | def submit(self, job, nonce2, solution):
325 | t = time.time()
326 |
327 | ret = yield from self.call('mining.submit',
328 | self.server.username,
329 | job.job_id,
330 | binascii.hexlify(job.ntime).decode('utf-8'),
331 | binascii.hexlify(nonce2).decode('utf-8'),
332 | binascii.hexlify(solution).decode('utf-8'))
333 | if ret['result'] == True:
334 | print("Share ACCEPTED in %.02fs" % (time.time() - t))
335 | else:
336 | print("Share REJECTED in %.02fs" % (time.time() - t))
337 |
338 | @coroutine
339 | def call(self, method, *params):
340 | msg_id = self.new_id()
341 | msg = {"id": msg_id,
342 | "method": method,
343 | "params": params}
344 |
345 | data = "%s\n" % json.dumps(msg)
346 | print('< %s' % data[:200] + (data[200:] and "...\n"), end='')
347 | self.writer.write(data.encode())
348 |
349 | try:
350 | #r = asyncio.ensure_future(self.notifier.wait_for(msg_id))
351 | r = asyncio.async(self.notifier.wait_for(msg_id))
352 | yield from asyncio.wait([r, self.notifier.task], timeout=30, return_when=asyncio.FIRST_COMPLETED)
353 |
354 | if self.notifier.task.done():
355 | raise self.notifier.task.exception()
356 |
357 | data = r.result()
358 | log = '> %s' % data
359 | print(log[:100] + (log[100:] and '...'))
360 |
361 | except TimeoutError:
362 | raise Exception("Request to server timed out.")
363 |
364 | return data
365 |
366 | def main():
367 | usage = "usage: %prog [OPTION]... SERVER[#tag]...\n" \
368 | "SERVER is one or more [stratum+tcp://]user:pass@host:port (required)\n" \
369 | "[#tag] is a per SERVER user friendly name displayed in stats (optional)\n" \
370 | "Example usage: %prog stratum+tcp://slush.miner1:password@zcash.slushpool.com:4444"
371 |
372 | parser = OptionParser(version=VERSION, usage=usage)
373 | parser.add_option('-g', '--disable-gui', dest='nogui', action='store_true', help='Disable graphical interface, use console only')
374 | parser.add_option('-c', '--cpu', dest='cpu', default=0, help='How many CPU solvers to start (-1=disabled, 0=auto)', type='int')
375 | parser.add_option('-n', '--nice', dest='nice', default=0, help="Niceness of the process (Linux only)", type='int')
376 |
377 | #parser.add_option('--verbose', dest='verbose', action='store_true', help='verbose output, suitable for redirection to log file')
378 | #parser.add_option('-q', '--quiet', dest='quiet', action='store_true', help='suppress all output except hash rate display')
379 | #parser.add_option('--proxy', dest='proxy', default='', help='specify as [[socks4|socks5|http://]user:pass@]host:port (default proto is socks5)')
380 | #parser.add_option('--no-ocl', dest='no_ocl', action='store_true', help="don't use OpenCL")
381 | #parser.add_option('-d', '--device', dest='device', default=[], help='comma separated device IDs, by default will use all (for OpenCL - only GPU devices)')
382 |
383 | #group = OptionGroup(parser, "Miner Options")
384 | #group.add_option('-r', '--rate', dest='rate', default=1, help='hash rate display interval in seconds, default=1 (60 with --verbose)', type='float')
385 | #group.add_option('-e', '--estimate', dest='estimate', default=900, help='estimated rate time window in seconds, default 900 (15 minutes)', type='int')
386 | #group.add_option('-t', '--tolerance', dest='tolerance', default=2, help='use fallback pool only after N consecutive connection errors, default 2', type='int')
387 | #group.add_option('-b', '--failback', dest='failback', default=60, help='attempt to fail back to the primary pool after N seconds, default 60', type='int')
388 | #group.add_option('--cutoff-temp', dest='cutoff_temp',default=[], help='AMD GPUs only. For GPUs requires github.com/mjmvisser/adl3. Comma separated temperatures at which to skip kernel execution, in C, default=95')
389 | #group.add_option('--cutoff-interval', dest='cutoff_interval',default=[], help='how long to not execute calculations if CUTOFF_TEMP is reached, in seconds, default=0.01')
390 | #group.add_option('--no-server-failbacks', dest='nsf', action='store_true', help='disable using failback hosts provided by server')
391 | #parser.add_option_group(group)
392 |
393 | #group = OptionGroup(parser,
394 | # "OpenCL Options",
395 | # "Every option except 'platform' and 'vectors' can be specified as a comma separated list. "
396 | # "If there aren't enough entries specified, the last available is used. "
397 | # "Use --vv to specify per-device vectors usage."
398 | #)
399 | #group.add_option('-p', '--platform', dest='platform', default=-1, help='use platform by id', type='int')
400 | #group.add_option('-w', '--worksize', dest='worksize', default=[], help='work group size, default is maximum returned by OpenCL')
401 | #group.add_option('-f', '--frames', dest='frames', default=[], help='will try to bring single kernel execution to 1/frames seconds, default=30, increase this for less desktop lag')
402 | #group.add_option('-s', '--sleep', dest='frameSleep', default=[], help='sleep per frame in seconds, default 0')
403 | #group.add_option('--vv', dest='vectors', default=[], help='use vectors, default false')
404 | #group.add_option('-v', '--vectors', dest='old_vectors',action='store_true', help='use vectors')
405 | #parser.add_option_group(group)
406 | #options.rate = max(options.rate, 60) if options.verbose else max(options.rate, 0.1)
407 | #options.max_update_time = 60
408 | #options.device = tokenize(options.device, 'device', [])
409 | #options.cutoff_temp = tokenize(options.cutoff_temp, 'cutoff_temp', [95], float)
410 | #options.cutoff_interval = tokenize(options.cutoff_interval, 'cutoff_interval', [0.01], float)
411 |
412 | (options, options.servers) = parser.parse_args()
413 | options.version = VERSION
414 |
415 | servers = [ Server.from_url(s) for s in options.servers]
416 | if len(servers) == 0:
417 | parser.print_usage()
418 | return
419 |
420 | if options.nice is not 0:
421 | print("Setting proces niceness to %d" % os.nice(options.nice))
422 |
423 | if options.cpu == -1:
424 | cpus = 0
425 | elif options.cpu == 0:
426 | cpus = multiprocessing.cpu_count()
427 | else:
428 | cpus = options.cpu
429 |
430 | global gui_enabled
431 | if options.nogui:
432 | gui_enabled = False
433 | elif not gui_enabled:
434 | print("GUI disabled, please install PySide/Qt first.")
435 |
436 | print("Using %d CPU solver instances" % cpus)
437 | print(servers)
438 |
439 | loop = asyncio.get_event_loop()
440 |
441 | solvers = SolverPool(loop, gpus=0, cpus=cpus)
442 | switcher = ServerSwitcher(loop, servers, solvers)
443 | loop.run_until_complete(switcher.run())
444 |
445 | loop.close()
446 |
447 | if __name__ == '__main__':
448 | main()
449 |
--------------------------------------------------------------------------------