Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
CFSworks committed Feb 6, 2012
0 parents commit a971b36
Show file tree
Hide file tree
Showing 26 changed files with 4,830 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.pyc
*.pyo
*.py~
*.elf
*.cfg
*.log
phoenix2/www
MANIFEST
build
dist
4 changes: 4 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
recursive-include phoenix2 *.py *.cl
recursive-include doc *.txt *.py
include phoenix2/www/TODO
include phoenix.py
147 changes: 147 additions & 0 deletions doc/cpu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# THIS IS AN EXAMPLE KERNEL FOR PHOENIX 2.
# THOUGH IT IS FULLY FUNCTIONAL, IT IS QUITE SLOW. IT IS INTENDED FOR
# DEMONSTRATION PURPOSES ONLY.

# Additionally, this doesn't demonstrate QueueReader, which is the preferred
# way of making kernels that dispatch a separate thread to handle NonceRanges
# (as this one does)

import time
from twisted.internet import reactor, defer

# You should really look at the functions defined here. They are useful.
from phoenix2.core.KernelInterface import KernelOption

# This class needs to be defined in all Phoenix 2 kernels. The required
# functions are __init__, start, and stop. Everything else is optional.
class MiningKernel(object):
# Here you can define options that the kernel will accept from the
# configuration file.
GREETING = KernelOption('greeting', str, default='',
help='Defines what debug message should be '
'printed at kernel start-up time.')

def __init__(self, interface):
self.interface = interface
self._stop = False

# Here we can ask the interface for the Phoenix 2 device ID, which is a
# lower-case (it is lower-cased by Phoenix even if caps are used in the
# configuration file) string identifying a specific device on the
# system.
# We don't need this for much, given that this is a CPU miner, but
# let's just show how to access it.
if not self.interface.getDeviceID().startswith('cpu:'):
self.interface.fatal('This kernel is only for CPUs!')
return

# Let's also print that configuration message that we set up before...
self.interface.debug('Greeting: ' + self.GREETING)

# We can also provide metadata on what the kernel is doing.
self.interface.setMeta('cores', '1')

@classmethod
def autodetect(cls, callback):
# This class method is used when Phoenix loads the kernel to autodetect
# the devices that it supports. When this function runs, the kernel is
# to look for all supported devices present on the system, and call
# callback(devid) for each one.
# It is also legal to store the callback and trigger the callback in
# the event of hotplug. If this is the case, the kernel must also
# define a class method called stopAutodetect() that disables hotplug
# detection.
# Also note that it is legal to call this function multiple times
# without calling stopAutodetect in between. If this function is called
# again, the kernel must redetect all devices present and send them all
# through the callback again, even the ones it has already detected.

# In this case, there is only one device this kernel supports: the CPU
# (which we know is present) - the CPU is identified by devid cpu:0 by
# default. The user can use cpu:1, etc, if he wishes to run several CPU
# kernels in tandem (for some reason), but the canonical ID for
# "the CPU" is always cpu:0.
callback('cpu:0')

@classmethod
def analyzeDevice(cls, devid):
# This class method is for analyzing how well a kernel will support a
# specific device to help Phoenix automatically choose kernels.
# It is to return a tuple: (suitability, config, ids)
# Where 'suitability' is a number in the following table:
# 0 - DO NOT USE THIS KERNEL
# 1 - WILL WORK AS A FALLBACK
# 2 - INTENDED USE FOR THIS CLASS OF HARDWARE
# 3 - OPTIMIZED FOR THIS BRAND OF HARDWARE
# 4 - OPTIMIZED FOR THIS SPECIFIC MODEL OF HARDWARE
# 5 - OPTIMIZED FOR THIS HARDWARE'S CURRENT CONFIGURATION
# (e.g. kernels that work well when clocks are low, etc)
# And config is a dictionary of recommended configuration values, which
# will get used unless the user explicitly disables autoconfiguration.
# Finally, ids is the list of IDs that the device is known by, with the
# "preferred" ID being the first one.

if devid.startswith('cpu:'):
return (1, {}, [devid])
else:
return (0, {}, [devid])

def start(self):
self._stop = False
reactor.callInThread(self.mine)

def stop(self):
self._stop = True

def _fetchRangeHelper(self, d):
# This function is a workaround for Twisted's threading model. The
# callFromThread function, which is necessary to invoke a function in
# the main thread, does not come back with return values. So, this
# function accepts a deferred, fetches some work, and fires the work
# through the deferred. QueueReader deals with all of this internally.
self.interface.fetchRange().chainDeferred(d)

# inlineCallbacks is a Twisted thing, it means you can do "x = yield y"
# where y is a Deferred, and it will pause your function until the Deferred
# fires back with a value
@defer.inlineCallbacks
def mine(self):
# This is rate-counting logic...
nonceCounter = 0
nonceTime = time.time()

while True:
d = defer.Deferred()
reactor.callFromThread(self._fetchRangeHelper, d)
nr = yield d
# Now we work on nr...
# This is defined in WorkQueue.py
for nonce in xrange(nr.base, nr.base+nr.size):
# Here we simply have to test nonce. We can do this ourselves,
# but the interface has a convenience function to do this for
# us. (It doesn't communicate elsewhere with Phoenix and is
# therefore safe to use without reactor.callFromThread)
hash = self.interface.calculateHash(nr.unit, nonce)

# There's also a convenience function for comparing the hash
# against the target.
if self.interface.checkTarget(hash, nr.unit.target):
# It's good! Let's send it in...
reactor.callFromThread(self.interface.foundNonce, nr.unit,
nonce)

# Count the nonce we just did, and report the rate, in
# kilohashes, to the interface.
nonceCounter += 1
if nonceCounter >= 0x100:
now = time.time()
dt = now - nonceTime
reactor.callFromThread(self.interface.updateRate,
int(nonceCounter/dt/1000))
nonceCounter = 0
nonceTime = now

# Finally, this thread needs to die if the kernel has been
# asked to stop...
if self._stop:
return
65 changes: 65 additions & 0 deletions doc/rpc.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
This is just a quick reference of all JSON-RPC calls available in the Phoenix 2
server:

getstatus() # Get general status on the server:
{'uptime': seconds,
'connection': {'type': ('rpc'|'rpclp'|'mmp'),
'connected': boolean,
'url': 'url'}
'results': {'accepted': 12345,
'rejected': 12345}
}


getrawconfig() # Download the entire configuration file as a single string.
setrawconfig(cfg) # Overwrite the configuration file with a string.

listdevices() # Returns an array of devices in this format:
{'id': minerID,
'name': 'miner name',
'status': ('running'|'suspended'|'disabled'),
'rate': khash,
'config': {'key1':'value1', 'key2':'value2'},
'meta': {'key1':'value1', 'key2':'value2'},
'uptime': seconds,
'results': 24690
}

getlogs(skip, limit) # Return logs, skipping 'skip' logs (if skip is negative,
# it will skip all but the last abs(n) logs),
# and limit result to 'limit' entries (or 0 for no limit)
{'id': minerID,
'timestamp': unix_time_seconds,
'msg': 'Message as it appears in the console, everything but the [time] [id]',
'type': ('result'|'fatal'|etc),
# if type=result
'details': {'accepted': boolean,
'hash': 'hexadecimal_here'}
# if type=fatal, error, etc
'details': {'error': 'Error message as from the kernel'}
...
}

setconfig(section, variable, value) # Alter a single configuration option.
getconfig(section, variable) # Retrive a configuration value.

redetect(terminate) # Re-run autodetection (useful after changing the
# autodetect config variable)
# 'terminate' specifies whether to also forget about
# autodetected kernels that don't meet with the new
# autodetect rules.
# It defaults to 'false' if not provided.

restart(minerID) # Restart a single miner. This also refreshes the
# configuration for that miner. Will not work for disabled
# kernels (you will need to use start(minerID) for this)
restart() # When used with no minerID, or minerID=null, it does it for every
# single running kernel.
disable(minerID) # Shut down the kernel and mark it as disabled in the config.
suspend(minerID) # Stop a miner temporarily. This is not saved in the config.
# The miner will resume when Phoenix is restarted.
suspend() # As with restart() - suspends every running kernel.
start(minerID) # Resume a miner from suspend or disable.
start() # Starts every suspended (but not disabled) kernel.

shutdown() # Stop the running Phoenix process.
37 changes: 37 additions & 0 deletions phoenix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env python

from twisted.internet import reactor
from phoenix2.core.PhoenixCore import PhoenixCore

import sys, os

def main():
if len(sys.argv) > 1:
cfg = sys.argv[1]
elif sys.platform == 'win32':
# The Windows users get their own special treatment. The script creates
# an empty configuration file for them if they don't specify one.
cfg = os.path.join(os.path.dirname(sys.argv[0]), 'phoenix.cfg')
if not os.path.isfile(cfg) or os.stat(cfg).st_size == 0:
print('-'*79)
print('It looks like this is your first time running Phoenix.')
#print('Please go to http://localhost:7780 in a web browser to'
# ' set it up.')
print('Please edit your phoenix.cfg file')
print('-'*79)
open(cfg, 'a').close()
else:
print('Please specify a configuration file.')
sys.exit()

if not os.path.isfile(cfg):
print('Error: %s does not exist.' % cfg)
sys.exit()

pc = PhoenixCore(cfg)
pc.start()

reactor.run()

if __name__ == '__main__':
main()
Empty file added phoenix2/__init__.py
Empty file.
51 changes: 51 additions & 0 deletions phoenix2/backend/ClientBase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Copyright (C) 2011 by jedi95 <[email protected]> and
# CFSworks <[email protected]>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import struct

class AssignedWork(object):
data = None
mask = None
target = None
maxtime = None
time = None
identifier = None
def setMaxTimeIncrement(self, n):
self.time = n
self.maxtime = struct.unpack('>I', self.data[68:72])[0] + n

class ClientBase(object):
callbacksActive = True

def _deactivateCallbacks(self):
"""Shut down the runCallback function. Typically used post-disconnect.
"""
self.callbacksActive = False

def runCallback(self, callback, *args):
"""Call the callback on the handler, if it's there, specifying args."""

if not self.callbacksActive:
return

func = getattr(self.handler, 'on' + callback.capitalize(), None)
if callable(func):
func(*args)
Loading

0 comments on commit a971b36

Please sign in to comment.