├── .gitignore ├── COPYING ├── Dockerfile ├── Makefile ├── README.md ├── SOAPpy ├── Client.py ├── Config.py ├── Errors.py ├── GSIServer.py ├── NS.py ├── Parser.py ├── SOAP.py ├── SOAPBuilder.py ├── Server.py ├── Types.py ├── URLopener.py ├── Utilities.py ├── WSDL.py ├── __init__.py └── version.py ├── conf ├── requirements-development.txt └── requirements-testing.txt ├── configure ├── dev ├── CLEAN ├── COUNT ├── COVERAGE_REPORT ├── COVERAGE_TEST ├── FIND ├── LINT ├── TEST ├── convert_networks ├── convert_networks.py └── readme ├── fpconst.py ├── litecoin_scrypt ├── scrypt.c ├── scrypt.h ├── scryptmodule.c └── setup.py ├── nattraverso ├── __init__.py ├── ipdiscover.py ├── portmapper.py ├── pynupnp │ ├── __init__.py │ ├── soap.py │ ├── upnp.py │ └── upnpxml.py └── utils.py ├── p2pool ├── __init__.py ├── bitcoin │ ├── __init__.py │ ├── data.py │ ├── getwork.py │ ├── height_tracker.py │ ├── helper.py │ ├── networks │ │ ├── __init__.py │ │ ├── bitcoin.py │ │ ├── bitcoin_testnet.py │ │ ├── bitcoincash.py │ │ ├── bitcoincash_testnet.py │ │ ├── bitcoinsv.py │ │ ├── bitcoinsv_testnet.py │ │ ├── btcregtest.py │ │ ├── fastcoin.py │ │ ├── litecoin.py │ │ ├── litecoin_testnet.py │ │ ├── namecoin.py │ │ ├── namecoin_testnet.py │ │ ├── terracoin.py │ │ └── terracoin_testnet.py │ ├── p2p.py │ ├── script.py │ ├── sha256.py │ ├── stratum.py │ └── worker_interface.py ├── data.py ├── main.py ├── networks │ ├── __init__.py │ ├── bitcoin.py │ ├── bitcoin_testnet.py │ ├── bitcoincash.py │ ├── bitcoincash_testnet.py │ ├── bitcoinsv.py │ ├── bitcoinsv_testnet.py │ ├── btcregtest.py │ ├── fastcoin.py │ ├── litecoin.py │ ├── litecoin_testnet.py │ ├── terracoin.py │ └── terracoin_testnet.py ├── node.py ├── p2p.py ├── test │ ├── __init__.py │ ├── bitcoin │ │ ├── __init__.py │ │ ├── test_data.py │ │ ├── test_getwork.py │ │ ├── test_p2p.py │ │ ├── test_script.py │ │ └── test_sha256.py │ ├── test_data.py │ ├── test_node.py │ ├── test_p2p.py │ └── util │ │ ├── __init__.py │ │ ├── test_cash_addr.py │ │ ├── test_datachunker.py │ │ ├── test_deferral.py │ │ ├── test_expiring_dict.py │ │ ├── test_forest.py │ │ ├── test_graph.py │ │ ├── test_math.py │ │ ├── test_pack.py │ │ ├── test_segwit_addr.py │ │ └── test_skiplist.py ├── util │ ├── __init__.py │ ├── cash_addr.py │ ├── datachunker.py │ ├── deferral.py │ ├── deferred_resource.py │ ├── expiring_dict.py │ ├── fixargparse.py │ ├── forest.py │ ├── graph.py │ ├── jsonrpc.py │ ├── logging.py │ ├── math.py │ ├── memoize.py │ ├── memory.py │ ├── p2protocol.py │ ├── pack.py │ ├── segwit_addr.py │ ├── skiplist.py │ ├── switchprotocol.py │ └── variable.py ├── web.py └── work.py ├── requirements.txt ├── run_p2pool.py ├── setup.py ├── web-static ├── d3.v2.min.js ├── favicon.ico ├── graphs.html ├── index.html └── share.html └── wstools ├── .cvsignore ├── MIMEAttachment.py ├── Namespaces.py ├── TimeoutSocket.py ├── UserTuple.py ├── Utility.py ├── WSDLTools.py ├── XMLSchema.py ├── XMLname.py ├── __init__.py ├── c14n.py ├── logging.py └── tests ├── .cvsignore ├── README ├── __init__.py ├── config.txt ├── schema.tar.gz ├── test_t1.py ├── test_wsdl.py ├── test_wstools.py ├── test_wstools_net.py └── xmethods.tar.gz /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo 3 | *~ 4 | 5 | /data/ 6 | /litecoin_scrypt/build/ 7 | /_trial_temp* 8 | Makefile.local 9 | .cache/ 10 | .pkglist 11 | .pyenv/ 12 | .coverage 13 | build/ 14 | ltc_scrypt.so 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for P2Pool Server 2 | 3 | FROM ubuntu:20.04 4 | 5 | LABEL maintainer David Parrish 6 | LABEL description="Dockerized P2Pool" 7 | 8 | WORKDIR /p2pool 9 | ENV P2POOL_HOME /p2pool/p2pool 10 | ENV P2POOL_REPO https://github.com/jtoomim/p2pool.git 11 | 12 | 13 | # Using installation instructions for Linux 14 | # https://github.com/jtoomim/p2pool/blob/155022fc942ec45778a95622766a9b11d923e76e/README.md 15 | # hadolint ignore=DL3003 16 | RUN apt-get update \ 17 | && apt-get -y --no-install-recommends install ca-certificates=20* pypy=7.* pypy-dev=7.* pypy-setuptools=44.* gcc=4:9.* build-essential=12.* git=1:2.* wget=1.* \ 18 | && apt-get clean \ 19 | && rm -rf /var/lib/apt/lists/* \ 20 | && mkdir $P2POOL_HOME \ 21 | && bash -c "wget https://bootstrap.pypa.io/ez_setup.py -O - | pypy" \ 22 | && rm setuptools-*.zip \ 23 | && wget https://pypi.python.org/packages/source/z/zope.interface/zope.interface-4.1.3.tar.gz#md5=9ae3d24c0c7415deb249dd1a132f0f79 \ 24 | && tar zxf zope.interface-4.1.3.tar.gz \ 25 | && cd /p2pool/zope.interface-4.1.3 \ 26 | && pypy setup.py install \ 27 | && cd /p2pool \ 28 | && rm -r zope.interface-4.1.3* \ 29 | && wget https://pypi.python.org/packages/source/T/Twisted/Twisted-15.4.0.tar.bz2 \ 30 | && tar jxf Twisted-15.4.0.tar.bz2 \ 31 | && cd /p2pool/Twisted-15.4.0 \ 32 | && pypy setup.py install \ 33 | && cd /p2pool \ 34 | && rm -r Twisted-15.4.0* 35 | 36 | COPY . $P2POOL_HOME 37 | 38 | WORKDIR $P2POOL_HOME 39 | ENTRYPOINT ["pypy", "run_p2pool.py"] 40 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # === makefile ------------------------------------------------------------=== 2 | 3 | ROOT=$(shell pwd) 4 | CACHE=${ROOT}/.cache 5 | PYENV=${ROOT}/.pyenv 6 | CONF=${ROOT}/conf 7 | APP_NAME=p2pool 8 | 9 | -include Makefile.local 10 | 11 | .PHONY: all 12 | all: python-env 13 | 14 | .PHONY: check 15 | check: all 16 | "${PYENV}"/bin/coverage run "${PYENV}"/bin/trial p2pool 17 | "${PYENV}"/bin/coverage xml -o build/report/coverage.xml 18 | 19 | .PHONY: run 20 | run: all 21 | "${PYENV}"/bin/python run_p2pool.py 22 | 23 | .PHONY: shell 24 | shell: all 25 | "${PYENV}"/bin/ipython 26 | 27 | .PHONY: mostlyclean 28 | mostlyclean: 29 | -rm -rf build 30 | -rm -rf .coverage 31 | 32 | .PHONY: clean 33 | clean: mostlyclean 34 | -rm -rf "${PYENV}" 35 | 36 | .PHONY: distclean 37 | distclean: clean 38 | -rm -rf "${CACHE}" 39 | 40 | .PHONY: maintainer-clean 41 | maintainer-clean: distclean 42 | @echo 'This command is intended for maintainers to use; it' 43 | @echo 'deletes files that may need special tools to rebuild.' 44 | 45 | .PHONY: dist 46 | dist: 47 | 48 | # ===--------------------------------------------------------------------=== 49 | 50 | ${CACHE}/pyenv/virtualenv-1.11.6.tar.gz: 51 | mkdir -p "${CACHE}"/pyenv 52 | curl -L 'https://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.11.6.tar.gz' >'$@' || { rm -f '$@'; exit 1; } 53 | 54 | ${CACHE}/pyenv/pyenv-1.11.6-base.tar.gz: ${CACHE}/pyenv/virtualenv-1.11.6.tar.gz 55 | -rm -rf "${PYENV}" 56 | mkdir -p "${PYENV}" 57 | 58 | # virtualenv is used to create a separate Python installation 59 | # for this project in ${PYENV}. 60 | tar \ 61 | -C "${CACHE}"/pyenv --gzip \ 62 | -xf "${CACHE}"/pyenv/virtualenv-1.11.6.tar.gz 63 | python "${CACHE}"/pyenv/virtualenv-1.11.6/virtualenv.py \ 64 | --clear \ 65 | --distribute \ 66 | --never-download \ 67 | --prompt="(${APP_NAME}) " \ 68 | "${PYENV}" 69 | -rm -rf "${CACHE}"/pyenv/virtualenv-1.11.6 70 | 71 | # Snapshot the Python environment 72 | tar -C "${PYENV}" --gzip -cf "$@" . 73 | rm -rf "${PYENV}" 74 | 75 | ${CACHE}/pyenv/pyenv-1.11.6-extras.tar.gz: ${CACHE}/pyenv/pyenv-1.11.6-base.tar.gz ${ROOT}/requirements.txt ${CONF}/requirements*.txt 76 | -rm -rf "${PYENV}" 77 | mkdir -p "${PYENV}" 78 | mkdir -p "${CACHE}"/pypi 79 | 80 | # Uncompress saved Python environment 81 | tar -C "${PYENV}" --gzip -xf "${CACHE}"/pyenv/pyenv-1.11.6-base.tar.gz 82 | find "${PYENV}" -not -type d -print0 >"${ROOT}"/.pkglist 83 | 84 | # readline is installed here to get around a bug on Mac OS X 85 | # which is causing readline to not build properly if installed 86 | # from pip, and the fact that a different package must be used 87 | # to support it on Windows/Cygwin. 88 | if [ "x`uname -s`" = "xCygwin" ]; then \ 89 | "${PYENV}"/bin/pip install pyreadline; \ 90 | else \ 91 | "${PYENV}"/bin/easy_install readline; \ 92 | fi 93 | 94 | # pip is used to install Python dependencies for this project. 95 | for reqfile in "${ROOT}"/requirements.txt \ 96 | "${CONF}"/requirements*.txt; do \ 97 | "${PYENV}"/bin/python "${PYENV}"/bin/pip install \ 98 | --download-cache="${CACHE}"/pypi \ 99 | -r "$$reqfile" || exit 1; \ 100 | done 101 | 102 | # Snapshot the Python environment 103 | cat "${ROOT}"/.pkglist | xargs -0 rm -rf 104 | tar -C "${PYENV}" --gzip -cf "$@" . 105 | rm -rf "${PYENV}" "${ROOT}"/.pkglist 106 | 107 | .PHONY: 108 | python-env: ${PYENV}/.stamp-h 109 | 110 | ${PYENV}/.stamp-h: ${CACHE}/pyenv/pyenv-1.11.6-base.tar.gz ${CACHE}/pyenv/pyenv-1.11.6-extras.tar.gz 111 | -rm -rf "${PYENV}" 112 | mkdir -p "${PYENV}" 113 | 114 | # Uncompress saved Python environment 115 | tar -C "${PYENV}" --gzip -xf "${CACHE}"/pyenv/pyenv-1.11.6-base.tar.gz 116 | tar -C "${PYENV}" --gzip -xf "${CACHE}"/pyenv/pyenv-1.11.6-extras.tar.gz 117 | 118 | # All done! 119 | touch "$@" 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **P2pool installation with pypy -- Windows** 2 | 3 | 4 | On Windows, pypy is only supported via the Windows Subsystem for Linux (WSL). P2pool on pypy on WSL is much faster than P2pool on 5 | CPython on native Windows. To install WSL, first follow the steps outlined here: 6 | 7 | 8 | https://msdn.microsoft.com/en-us/commandline/wsl/install_guide 9 | 10 | 11 | Once you've done that, run bash and follow the rest of the steps below. 12 | 13 | 14 | **P2pool installation with pypy -- Linux and Windows** 15 | 16 | 17 | Copy and paste the following commands into a bash shell in order to install p2pool on Windows or Linux. 18 | 19 | >sudo apt-get update 20 | 21 | >sudo apt-get -y install pypy pypy-dev pypy-setuptools gcc build-essential git 22 | 23 | 24 | >wget https://bootstrap.pypa.io/ez_setup.py -O - | sudo pypy 25 | >sudo rm setuptools-*.zip 26 | 27 | 28 | >wget https://pypi.python.org/packages/source/z/zope.interface/zope.interface-4.1.3.tar.gz#md5=9ae3d24c0c7415deb249dd1a132f0f79 29 | 30 | >tar zxf zope.interface-4.1.3.tar.gz 31 | 32 | >cd zope.interface-4.1.3/ 33 | 34 | >sudo pypy setup.py install 35 | 36 | >cd .. 37 | 38 | >sudo rm -r zope.interface-4.1.3* 39 | 40 | 41 | >wget https://pypi.python.org/packages/source/T/Twisted/Twisted-15.4.0.tar.bz2 42 | 43 | >tar jxf Twisted-15.4.0.tar.bz2 44 | 45 | >cd Twisted-15.4.0 46 | 47 | >sudo pypy setup.py install 48 | 49 | >cd .. 50 | 51 | >sudo rm -r Twisted-15.4.0* 52 | 53 | 54 | >git clone https://github.com/jtoomim/p2pool.git 55 | 56 | >cd p2pool 57 | 58 | 59 | You'll also need to install and run your bitcoind or altcoind of choice, and edit ~/.bitcoin/bitcoin.conf (or the corresponding file for litecoin or whatever other coin you intend to mine) with your bitcoind's RPC username and password. Launch your bitcoind or altcoind, and after it has finished downloading blocks and syncing, go to your p2pool directory and run 60 | 61 | 62 | >pypy run_p2pool.py 63 | 64 | **P2pool Dockerfile 65 | 66 | The Dockerfile uses the build instruction above to create a Docker image. 67 | 68 | Build Docker image named `p2pool` 69 | 70 | >docker build -t p2pool ./ 71 | 72 | Run p2pool from Docker image 73 | 74 | >docker run -it --rm p2pool --version 75 | 76 | **jtoomimnet vs mainnet** 77 | 78 | 79 | If you wish to use the original forrestv btc mainnet instead of jtoomimnet, then replace 80 | 81 | 82 | >git clone https://github.com/jtoomim/p2pool.git 83 | 84 | >cd p2pool 85 | 86 | 87 | above with 88 | 89 | 90 | >git clone https://github.com/p2pool/p2pool.git 91 | 92 | >cd p2pool 93 | 94 | 95 | Note: The BTC p2pools currently have low hashrate, which means that payouts will be infrequent, large, and unpredictable. As of Feb 2018, blocks are found on jtoomimnet on average once every 25 days, and blocks are found on mainnet on average once every 108 days. Do not mine on BTC p2pool unless you are very patient and can tolerate receiving no revenue for several months. 96 | 97 | 98 | **Miner setup** 99 | 100 | 101 | P2pool communicates with miners via the stratum protocol. For BTC, configure your miners with the following information: 102 | 103 | 104 | >URL: stratum+tcp://(Your node's IP address or hostname):9332 105 | 106 | >Worker: (Your bitcoin address) 107 | 108 | >Password: x 109 | 110 | 111 | 112 | Mining to Legacy (P2PKH), SegWit/MultiSig (P2SH) and Bech32 addresses are supported for the following coins with the specified address prefixes: 113 | 114 | |Coin |P2PKH |P2SH |Bech32 | 115 | |---------------|-------|-------|-------------------------------| 116 | |Bitcoin |`1...` |`3...` |`bc1...` | 117 | |Bitcoin Cash* |`1...` | (test)|`bitcoincash:q...` or `q...` | 118 | |Bitcoin SV* |`1...` | (test)|`bitcoincash:q...` or `q...` | 119 | |Litecoin |`L...` |`M...` |`ltc1...` | 120 | * Bitcoin Cash and Bitcoin SV uses cashaddr instead of Bech32 121 | 122 | **Only Legacy addresses (P2PKH) are supported for coins not mentioned above. If you use an address that p2pool cannot understand, then p2pool will mine to that node's default address instead.** 123 | 124 | 125 | If you wish to modify the mining difficulty, you may add something like "address+4096" after your mining address to set the pseudoshare difficulty to 4096, or "address/65536" to set the actual share difficulty to 65536 or the p2pool minimum share difficulty, whichever is higher. Pseudoshares only affect hashrate statistics, whereas actual shares affect revenue variance and efficiency. 126 | 127 | 128 | **Firewall considerations** 129 | 130 | 131 | If your node is behind a firewall or behind NAT (i.e. on a private IP address), you may want to forward ports to your p2pool server. P2pool uses two ports: one for p2p communication with the p2pool network, and another for both the web UI and for stratum communication with workers. For Bitcoin, those ports are 9333 (p2p) and 9332 (stratum/web). For Litecoin, they are 9326 (p2p) and 9327 (stratum/web). For Bitcoin Cash, they are 9349 (p2p) and 9348 (stratum/web). 132 | -------------------------------------------------------------------------------- /SOAPpy/Errors.py: -------------------------------------------------------------------------------- 1 | """ 2 | ################################################################################ 3 | # 4 | # SOAPpy - Cayce Ullman (cayce@actzero.com) 5 | # Brian Matthews (blm@actzero.com) 6 | # Gregory Warnes (Gregory.R.Warnes@Pfizer.com) 7 | # Christopher Blunck (blunck@gst.com) 8 | # 9 | ################################################################################ 10 | # Copyright (c) 2003, Pfizer 11 | # Copyright (c) 2001, Cayce Ullman. 12 | # Copyright (c) 2001, Brian Matthews. 13 | # 14 | # All rights reserved. 15 | # 16 | # Redistribution and use in source and binary forms, with or without 17 | # modification, are permitted provided that the following conditions are met: 18 | # Redistributions of source code must retain the above copyright notice, this 19 | # list of conditions and the following disclaimer. 20 | # 21 | # Redistributions in binary form must reproduce the above copyright notice, 22 | # this list of conditions and the following disclaimer in the documentation 23 | # and/or other materials provided with the distribution. 24 | # 25 | # Neither the name of actzero, inc. nor the names of its contributors may 26 | # be used to endorse or promote products derived from this software without 27 | # specific prior written permission. 28 | # 29 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 30 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 | # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 33 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 34 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 35 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 36 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 38 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 | # 40 | ################################################################################ 41 | """ 42 | 43 | ident = '$Id: Errors.py 921 2005-02-15 16:32:23Z warnes $' 44 | from version import __version__ 45 | 46 | import exceptions 47 | 48 | ################################################################################ 49 | # Exceptions 50 | ################################################################################ 51 | class Error(exceptions.Exception): 52 | def __init__(self, msg): 53 | self.msg = msg 54 | def __str__(self): 55 | return "" % self.msg 56 | __repr__ = __str__ 57 | def __call__(self): 58 | return (msg,) 59 | 60 | class RecursionError(Error): 61 | pass 62 | 63 | class UnknownTypeError(Error): 64 | pass 65 | 66 | class HTTPError(Error): 67 | # indicates an HTTP protocol error 68 | def __init__(self, code, msg): 69 | self.code = code 70 | self.msg = msg 71 | def __str__(self): 72 | return "" % (self.code, self.msg) 73 | __repr__ = __str__ 74 | def __call___(self): 75 | return (self.code, self.msg, ) 76 | 77 | class UnderflowError(exceptions.ArithmeticError): 78 | pass 79 | 80 | -------------------------------------------------------------------------------- /SOAPpy/GSIServer.py: -------------------------------------------------------------------------------- 1 | from __future__ import nested_scopes 2 | 3 | """ 4 | GSIServer - Contributed by Ivan R. Judson 5 | 6 | 7 | ################################################################################ 8 | # 9 | # SOAPpy - Cayce Ullman (cayce@actzero.com) 10 | # Brian Matthews (blm@actzero.com) 11 | # Gregory Warnes (Gregory.R.Warnes@Pfizer.com) 12 | # Christopher Blunck (blunck@gst.com) 13 | # 14 | ################################################################################ 15 | # Copyright (c) 2003, Pfizer 16 | # Copyright (c) 2001, Cayce Ullman. 17 | # Copyright (c) 2001, Brian Matthews. 18 | # 19 | # All rights reserved. 20 | # 21 | # Redistribution and use in source and binary forms, with or without 22 | # modification, are permitted provided that the following conditions are met: 23 | # Redistributions of source code must retain the above copyright notice, this 24 | # list of conditions and the following disclaimer. 25 | # 26 | # Redistributions in binary form must reproduce the above copyright notice, 27 | # this list of conditions and the following disclaimer in the documentation 28 | # and/or other materials provided with the distribution. 29 | # 30 | # Neither the name of actzero, inc. nor the names of its contributors may 31 | # be used to endorse or promote products derived from this software without 32 | # specific prior written permission. 33 | # 34 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 35 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 36 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 37 | # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 38 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 39 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 40 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 41 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 42 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 43 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 44 | # 45 | ################################################################################ 46 | """ 47 | 48 | ident = '$Id: GSIServer.py 1468 2008-05-24 01:55:33Z warnes $' 49 | from version import __version__ 50 | 51 | #import xml.sax 52 | import re 53 | import socket 54 | import sys 55 | import SocketServer 56 | from types import * 57 | import BaseHTTPServer 58 | 59 | # SOAPpy modules 60 | from Parser import parseSOAPRPC 61 | from Config import SOAPConfig 62 | from Types import faultType, voidType, simplify 63 | from NS import NS 64 | from SOAPBuilder import buildSOAP 65 | from Utilities import debugHeader, debugFooter 66 | 67 | try: from M2Crypto import SSL 68 | except: pass 69 | 70 | ##### 71 | 72 | from Server import * 73 | 74 | from pyGlobus.io import GSITCPSocketServer, ThreadingGSITCPSocketServer 75 | from pyGlobus import ioc 76 | 77 | def GSIConfig(): 78 | config = SOAPConfig() 79 | config.channel_mode = ioc.GLOBUS_IO_SECURE_CHANNEL_MODE_GSI_WRAP 80 | config.delegation_mode = ioc.GLOBUS_IO_SECURE_DELEGATION_MODE_FULL_PROXY 81 | config.tcpAttr = None 82 | config.authMethod = "_authorize" 83 | return config 84 | 85 | Config = GSIConfig() 86 | 87 | class GSISOAPServer(GSITCPSocketServer, SOAPServerBase): 88 | def __init__(self, addr = ('localhost', 8000), 89 | RequestHandler = SOAPRequestHandler, log = 0, 90 | encoding = 'UTF-8', config = Config, namespace = None): 91 | 92 | # Test the encoding, raising an exception if it's not known 93 | if encoding != None: 94 | ''.encode(encoding) 95 | 96 | self.namespace = namespace 97 | self.objmap = {} 98 | self.funcmap = {} 99 | self.encoding = encoding 100 | self.config = config 101 | self.log = log 102 | 103 | self.allow_reuse_address= 1 104 | 105 | GSITCPSocketServer.__init__(self, addr, RequestHandler, 106 | self.config.channel_mode, 107 | self.config.delegation_mode, 108 | tcpAttr = self.config.tcpAttr) 109 | 110 | def get_request(self): 111 | sock, addr = GSITCPSocketServer.get_request(self) 112 | 113 | return sock, addr 114 | 115 | class ThreadingGSISOAPServer(ThreadingGSITCPSocketServer, SOAPServerBase): 116 | 117 | def __init__(self, addr = ('localhost', 8000), 118 | RequestHandler = SOAPRequestHandler, log = 0, 119 | encoding = 'UTF-8', config = Config, namespace = None): 120 | 121 | # Test the encoding, raising an exception if it's not known 122 | if encoding != None: 123 | ''.encode(encoding) 124 | 125 | self.namespace = namespace 126 | self.objmap = {} 127 | self.funcmap = {} 128 | self.encoding = encoding 129 | self.config = config 130 | self.log = log 131 | 132 | self.allow_reuse_address= 1 133 | 134 | ThreadingGSITCPSocketServer.__init__(self, addr, RequestHandler, 135 | self.config.channel_mode, 136 | self.config.delegation_mode, 137 | tcpAttr = self.config.tcpAttr) 138 | 139 | def get_request(self): 140 | sock, addr = ThreadingGSITCPSocketServer.get_request(self) 141 | 142 | return sock, addr 143 | 144 | -------------------------------------------------------------------------------- /SOAPpy/NS.py: -------------------------------------------------------------------------------- 1 | from __future__ import nested_scopes 2 | 3 | """ 4 | ################################################################################ 5 | # 6 | # SOAPpy - Cayce Ullman (cayce@actzero.com) 7 | # Brian Matthews (blm@actzero.com) 8 | # Gregory Warnes (Gregory.R.Warnes@Pfizer.com) 9 | # Christopher Blunck (blunck@gst.com) 10 | # 11 | ################################################################################ 12 | # Copyright (c) 2003, Pfizer 13 | # Copyright (c) 2001, Cayce Ullman. 14 | # Copyright (c) 2001, Brian Matthews. 15 | # 16 | # All rights reserved. 17 | # 18 | # Redistribution and use in source and binary forms, with or without 19 | # modification, are permitted provided that the following conditions are met: 20 | # Redistributions of source code must retain the above copyright notice, this 21 | # list of conditions and the following disclaimer. 22 | # 23 | # Redistributions in binary form must reproduce the above copyright notice, 24 | # this list of conditions and the following disclaimer in the documentation 25 | # and/or other materials provided with the distribution. 26 | # 27 | # Neither the name of actzero, inc. nor the names of its contributors may 28 | # be used to endorse or promote products derived from this software without 29 | # specific prior written permission. 30 | # 31 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 32 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 33 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 34 | # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 35 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 36 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 37 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 38 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 39 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 40 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41 | # 42 | ################################################################################ 43 | """ 44 | 45 | ident = '$Id: NS.py 1468 2008-05-24 01:55:33Z warnes $' 46 | from version import __version__ 47 | 48 | ############################################################################## 49 | # Namespace Class 50 | ################################################################################ 51 | def invertDict(dict): 52 | d = {} 53 | 54 | for k, v in dict.items(): 55 | d[v] = k 56 | 57 | return d 58 | 59 | class NS: 60 | XML = "http://www.w3.org/XML/1998/namespace" 61 | 62 | ENV = "http://schemas.xmlsoap.org/soap/envelope/" 63 | ENC = "http://schemas.xmlsoap.org/soap/encoding/" 64 | 65 | XSD = "http://www.w3.org/1999/XMLSchema" 66 | XSD2 = "http://www.w3.org/2000/10/XMLSchema" 67 | XSD3 = "http://www.w3.org/2001/XMLSchema" 68 | 69 | XSD_L = [XSD, XSD2, XSD3] 70 | EXSD_L= [ENC, XSD, XSD2, XSD3] 71 | 72 | XSI = "http://www.w3.org/1999/XMLSchema-instance" 73 | XSI2 = "http://www.w3.org/2000/10/XMLSchema-instance" 74 | XSI3 = "http://www.w3.org/2001/XMLSchema-instance" 75 | XSI_L = [XSI, XSI2, XSI3] 76 | 77 | URN = "http://soapinterop.org/xsd" 78 | 79 | # For generated messages 80 | XML_T = "xml" 81 | ENV_T = "SOAP-ENV" 82 | ENC_T = "SOAP-ENC" 83 | XSD_T = "xsd" 84 | XSD2_T= "xsd2" 85 | XSD3_T= "xsd3" 86 | XSI_T = "xsi" 87 | XSI2_T= "xsi2" 88 | XSI3_T= "xsi3" 89 | URN_T = "urn" 90 | 91 | NSMAP = {ENV_T: ENV, ENC_T: ENC, XSD_T: XSD, XSD2_T: XSD2, 92 | XSD3_T: XSD3, XSI_T: XSI, XSI2_T: XSI2, XSI3_T: XSI3, 93 | URN_T: URN} 94 | NSMAP_R = invertDict(NSMAP) 95 | 96 | STMAP = {'1999': (XSD_T, XSI_T), '2000': (XSD2_T, XSI2_T), 97 | '2001': (XSD3_T, XSI3_T)} 98 | STMAP_R = invertDict(STMAP) 99 | 100 | def __init__(self): 101 | raise Error, "Don't instantiate this" 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /SOAPpy/SOAP.py: -------------------------------------------------------------------------------- 1 | """This file is here for backward compatibility with versions <= 0.9.9 2 | 3 | Delete when 1.0.0 is released! 4 | """ 5 | 6 | ident = '$Id: SOAP.py 541 2004-01-31 04:20:06Z warnes $' 7 | from version import __version__ 8 | 9 | from Client import * 10 | from Config import * 11 | from Errors import * 12 | from NS import * 13 | from Parser import * 14 | from SOAPBuilder import * 15 | from Server import * 16 | from Types import * 17 | from Utilities import * 18 | import wstools 19 | import WSDL 20 | 21 | from warnings import warn 22 | 23 | warn(""" 24 | 25 | The sub-module SOAPpy.SOAP is deprecated and is only 26 | provided for short-term backward compatibility. Objects are now 27 | available directly within the SOAPpy module. Thus, instead of 28 | 29 | from SOAPpy import SOAP 30 | ... 31 | SOAP.SOAPProxy(...) 32 | 33 | use 34 | 35 | from SOAPpy import SOAPProxy 36 | ... 37 | SOAPProxy(...) 38 | 39 | instead. 40 | """, DeprecationWarning) 41 | -------------------------------------------------------------------------------- /SOAPpy/URLopener.py: -------------------------------------------------------------------------------- 1 | """Provide a class for loading data from URL's that handles basic 2 | authentication""" 3 | 4 | ident = '$Id: URLopener.py 541 2004-01-31 04:20:06Z warnes $' 5 | from version import __version__ 6 | 7 | from Config import Config 8 | from urllib import FancyURLopener 9 | 10 | class URLopener(FancyURLopener): 11 | 12 | username = None 13 | passwd = None 14 | 15 | 16 | def __init__(self, username=None, passwd=None, *args, **kw): 17 | FancyURLopener.__init__( self, *args, **kw) 18 | self.username = username 19 | self.passwd = passwd 20 | 21 | 22 | def prompt_user_passwd(self, host, realm): 23 | return self.username, self.passwd 24 | -------------------------------------------------------------------------------- /SOAPpy/Utilities.py: -------------------------------------------------------------------------------- 1 | """ 2 | ################################################################################ 3 | # Copyright (c) 2003, Pfizer 4 | # Copyright (c) 2001, Cayce Ullman. 5 | # Copyright (c) 2001, Brian Matthews. 6 | # 7 | # All rights reserved. 8 | # 9 | # Redistribution and use in source and binary forms, with or without 10 | # modification, are permitted provided that the following conditions are met: 11 | # Redistributions of source code must retain the above copyright notice, this 12 | # list of conditions and the following disclaimer. 13 | # 14 | # Redistributions in binary form must reproduce the above copyright notice, 15 | # this list of conditions and the following disclaimer in the documentation 16 | # and/or other materials provided with the distribution. 17 | # 18 | # Neither the name of actzero, inc. nor the names of its contributors may 19 | # be used to endorse or promote products derived from this software without 20 | # specific prior written permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 26 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 29 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | # 33 | ################################################################################ 34 | """ 35 | 36 | ident = '$Id: Utilities.py 1298 2006-11-07 00:54:15Z sanxiyn $' 37 | from version import __version__ 38 | 39 | import re 40 | import string 41 | import sys 42 | from types import * 43 | 44 | # SOAPpy modules 45 | from Errors import * 46 | 47 | ################################################################################ 48 | # Utility infielders 49 | ################################################################################ 50 | def collapseWhiteSpace(s): 51 | return re.sub('\s+', ' ', s).strip() 52 | 53 | def decodeHexString(data): 54 | conv = { 55 | '0': 0x0, '1': 0x1, '2': 0x2, '3': 0x3, '4': 0x4, 56 | '5': 0x5, '6': 0x6, '7': 0x7, '8': 0x8, '9': 0x9, 57 | 58 | 'a': 0xa, 'b': 0xb, 'c': 0xc, 'd': 0xd, 'e': 0xe, 59 | 'f': 0xf, 60 | 61 | 'A': 0xa, 'B': 0xb, 'C': 0xc, 'D': 0xd, 'E': 0xe, 62 | 'F': 0xf, 63 | } 64 | 65 | ws = string.whitespace 66 | 67 | bin = '' 68 | 69 | i = 0 70 | 71 | while i < len(data): 72 | if data[i] not in ws: 73 | break 74 | i += 1 75 | 76 | low = 0 77 | 78 | while i < len(data): 79 | c = data[i] 80 | 81 | if c in string.whitespace: 82 | break 83 | 84 | try: 85 | c = conv[c] 86 | except KeyError: 87 | raise ValueError, \ 88 | "invalid hex string character `%s'" % c 89 | 90 | if low: 91 | bin += chr(high * 16 + c) 92 | low = 0 93 | else: 94 | high = c 95 | low = 1 96 | 97 | i += 1 98 | 99 | if low: 100 | raise ValueError, "invalid hex string length" 101 | 102 | while i < len(data): 103 | if data[i] not in string.whitespace: 104 | raise ValueError, \ 105 | "invalid hex string character `%s'" % c 106 | 107 | i += 1 108 | 109 | return bin 110 | 111 | def encodeHexString(data): 112 | h = '' 113 | 114 | for i in data: 115 | h += "%02X" % ord(i) 116 | 117 | return h 118 | 119 | def leapMonth(year, month): 120 | return month == 2 and \ 121 | year % 4 == 0 and \ 122 | (year % 100 != 0 or year % 400 == 0) 123 | 124 | def cleanDate(d, first = 0): 125 | ranges = (None, (1, 12), (1, 31), (0, 23), (0, 59), (0, 61)) 126 | months = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) 127 | names = ('year', 'month', 'day', 'hours', 'minutes', 'seconds') 128 | 129 | if len(d) != 6: 130 | raise ValueError, "date must have 6 elements" 131 | 132 | for i in range(first, 6): 133 | s = d[i] 134 | 135 | if type(s) == FloatType: 136 | if i < 5: 137 | try: 138 | s = int(s) 139 | except OverflowError: 140 | if i > 0: 141 | raise 142 | s = long(s) 143 | 144 | if s != d[i]: 145 | raise ValueError, "%s must be integral" % names[i] 146 | 147 | d[i] = s 148 | elif type(s) == LongType: 149 | try: s = int(s) 150 | except: pass 151 | elif type(s) != IntType: 152 | raise TypeError, "%s isn't a valid type" % names[i] 153 | 154 | if i == first and s < 0: 155 | continue 156 | 157 | if ranges[i] != None and \ 158 | (s < ranges[i][0] or ranges[i][1] < s): 159 | raise ValueError, "%s out of range" % names[i] 160 | 161 | if first < 6 and d[5] >= 61: 162 | raise ValueError, "seconds out of range" 163 | 164 | if first < 2: 165 | leap = first < 1 and leapMonth(d[0], d[1]) 166 | 167 | if d[2] > months[d[1]] + leap: 168 | raise ValueError, "day out of range" 169 | 170 | def debugHeader(title): 171 | s = '*** ' + title + ' ' 172 | print s + ('*' * (72 - len(s))) 173 | 174 | def debugFooter(title): 175 | print '*' * 72 176 | sys.stdout.flush() 177 | -------------------------------------------------------------------------------- /SOAPpy/WSDL.py: -------------------------------------------------------------------------------- 1 | """Parse web services description language to get SOAP methods. 2 | 3 | Rudimentary support.""" 4 | 5 | ident = '$Id: WSDL.py 1467 2008-05-16 23:32:51Z warnes $' 6 | from version import __version__ 7 | 8 | import wstools 9 | import xml 10 | from Errors import Error 11 | from Client import SOAPProxy, SOAPAddress 12 | from Config import Config 13 | import urllib 14 | 15 | class Proxy: 16 | """WSDL Proxy. 17 | 18 | SOAPProxy wrapper that parses method names, namespaces, soap actions from 19 | the web service description language (WSDL) file passed into the 20 | constructor. The WSDL reference can be passed in as a stream, an url, a 21 | file name, or a string. 22 | 23 | Loads info into self.methods, a dictionary with methodname keys and values 24 | of WSDLTools.SOAPCallinfo. 25 | 26 | For example, 27 | 28 | url = 'http://www.xmethods.org/sd/2001/TemperatureService.wsdl' 29 | wsdl = WSDL.Proxy(url) 30 | print len(wsdl.methods) # 1 31 | print wsdl.methods.keys() # getTemp 32 | 33 | 34 | See WSDLTools.SOAPCallinfo for more info on each method's attributes. 35 | """ 36 | 37 | def __init__(self, wsdlsource, config=Config, **kw ): 38 | 39 | reader = wstools.WSDLTools.WSDLReader() 40 | self.wsdl = None 41 | 42 | # From Mark Pilgrim's "Dive Into Python" toolkit.py--open anything. 43 | if self.wsdl is None and hasattr(wsdlsource, "read"): 44 | print 'stream:', wsdlsource 45 | try: 46 | self.wsdl = reader.loadFromStream(wsdlsource) 47 | except xml.parsers.expat.ExpatError, e: 48 | newstream = urllib.URLopener(key_file=config.SSL.key_file, cert_file=config.SSL.cert_file).open(wsdlsource) 49 | buf = newstream.readlines() 50 | raise Error, "Unable to parse WSDL file at %s: \n\t%s" % \ 51 | (wsdlsource, "\t".join(buf)) 52 | 53 | 54 | # NOT TESTED (as of April 17, 2003) 55 | #if self.wsdl is None and wsdlsource == '-': 56 | # import sys 57 | # self.wsdl = reader.loadFromStream(sys.stdin) 58 | # print 'stdin' 59 | 60 | if self.wsdl is None: 61 | try: 62 | file(wsdlsource) 63 | self.wsdl = reader.loadFromFile(wsdlsource) 64 | #print 'file' 65 | except (IOError, OSError): pass 66 | except xml.parsers.expat.ExpatError, e: 67 | newstream = urllib.urlopen(wsdlsource) 68 | buf = newstream.readlines() 69 | raise Error, "Unable to parse WSDL file at %s: \n\t%s" % \ 70 | (wsdlsource, "\t".join(buf)) 71 | 72 | if self.wsdl is None: 73 | try: 74 | stream = urllib.URLopener(key_file=config.SSL.key_file, cert_file=config.SSL.cert_file).open(wsdlsource) 75 | self.wsdl = reader.loadFromStream(stream, wsdlsource) 76 | except (IOError, OSError): pass 77 | except xml.parsers.expat.ExpatError, e: 78 | newstream = urllib.urlopen(wsdlsource) 79 | buf = newstream.readlines() 80 | raise Error, "Unable to parse WSDL file at %s: \n\t%s" % \ 81 | (wsdlsource, "\t".join(buf)) 82 | 83 | if self.wsdl is None: 84 | import StringIO 85 | self.wsdl = reader.loadFromString(str(wsdlsource)) 86 | #print 'string' 87 | 88 | # Package wsdl info as a dictionary of remote methods, with method name 89 | # as key (based on ServiceProxy.__init__ in ZSI library). 90 | self.methods = {} 91 | service = self.wsdl.services[0] 92 | port = service.ports[0] 93 | name = service.name 94 | binding = port.getBinding() 95 | portType = binding.getPortType() 96 | for operation in portType.operations: 97 | callinfo = wstools.WSDLTools.callInfoFromWSDL(port, operation.name) 98 | self.methods[callinfo.methodName] = callinfo 99 | 100 | self.soapproxy = SOAPProxy('http://localhost/dummy.webservice', 101 | config=config, **kw) 102 | 103 | def __str__(self): 104 | s = '' 105 | for method in self.methods.values(): 106 | s += str(method) 107 | return s 108 | 109 | def __getattr__(self, name): 110 | """Set up environment then let parent class handle call. 111 | 112 | Raises AttributeError is method name is not found.""" 113 | 114 | if not self.methods.has_key(name): raise AttributeError, name 115 | 116 | callinfo = self.methods[name] 117 | self.soapproxy.proxy = SOAPAddress(callinfo.location) 118 | self.soapproxy.namespace = callinfo.namespace 119 | self.soapproxy.soapaction = callinfo.soapAction 120 | return self.soapproxy.__getattr__(name) 121 | 122 | def show_methods(self): 123 | for key in self.methods.keys(): 124 | method = self.methods[key] 125 | print "Method Name:", key.ljust(15) 126 | print 127 | inps = method.inparams 128 | for parm in range(len(inps)): 129 | details = inps[parm] 130 | print " In #%d: %s (%s)" % (parm, details.name, details.type) 131 | print 132 | outps = method.outparams 133 | for parm in range(len(outps)): 134 | details = outps[parm] 135 | print " Out #%d: %s (%s)" % (parm, details.name, details.type) 136 | print 137 | 138 | -------------------------------------------------------------------------------- /SOAPpy/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | ident = '$Id: __init__.py 541 2004-01-31 04:20:06Z warnes $' 3 | from version import __version__ 4 | 5 | from Client import * 6 | from Config import * 7 | from Errors import * 8 | from NS import * 9 | from Parser import * 10 | from SOAPBuilder import * 11 | from Server import * 12 | from Types import * 13 | from Utilities import * 14 | import wstools 15 | import WSDL 16 | -------------------------------------------------------------------------------- /SOAPpy/version.py: -------------------------------------------------------------------------------- 1 | __version__="0.12.5" 2 | 3 | -------------------------------------------------------------------------------- /conf/requirements-development.txt: -------------------------------------------------------------------------------- 1 | ipdb==0.8 2 | ipython==2.2.0 3 | -------------------------------------------------------------------------------- /conf/requirements-testing.txt: -------------------------------------------------------------------------------- 1 | coverage==3.7.1 2 | unittest-xml-reporting==1.9.0 3 | unittest2==0.5.1 4 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # === configure -----------------------------------------------------------=== 4 | 5 | touch Makefile.local 6 | 7 | # ===--------------------------------------------------------------------=== 8 | # End of File 9 | # ===--------------------------------------------------------------------=== 10 | -------------------------------------------------------------------------------- /dev/CLEAN: -------------------------------------------------------------------------------- 1 | find p2pool/ -iname '*.py' | xargs pycl 2 | -------------------------------------------------------------------------------- /dev/COUNT: -------------------------------------------------------------------------------- 1 | find p2pool/ -not -path 'p2pool/test/*' -path '*.py' | xargs wc | sort -n 2 | -------------------------------------------------------------------------------- /dev/COVERAGE_REPORT: -------------------------------------------------------------------------------- 1 | python -m coverage report --include='p2pool/*' --omit='p2pool/test/*' -m | sort -k 3 -n 2 | -------------------------------------------------------------------------------- /dev/COVERAGE_TEST: -------------------------------------------------------------------------------- 1 | python -m coverage run `which trial` p2pool p2pool.main 2 | -------------------------------------------------------------------------------- /dev/FIND: -------------------------------------------------------------------------------- 1 | find p2pool/ -iname '*py' | xargs grep "$@" 2 | -------------------------------------------------------------------------------- /dev/LINT: -------------------------------------------------------------------------------- 1 | pylint p2pool/ -E | grep -v reactor | grep -v hashlib | grep -v 'Yield outside function' | grep -v 'function already defined' 2 | -------------------------------------------------------------------------------- /dev/TEST: -------------------------------------------------------------------------------- 1 | while true ; do 2 | find -iname '*.pyc' | xargs rm 3 | trial p2pool 4 | git checkout HEAD^ 5 | done 6 | -------------------------------------------------------------------------------- /dev/convert_networks: -------------------------------------------------------------------------------- 1 | #!/bin/bash -xe 2 | 3 | mkdir -p p2pool/networks/ 4 | cat > p2pool/networks/__init__.py << END 5 | import pkgutil 6 | 7 | nets = dict((name, __import__(name, globals(), fromlist="dummy")) 8 | for module_loader, name, ispkg in pkgutil.iter_modules(__path__)) 9 | for net_name, net in nets.iteritems(): 10 | net.NAME = net_name 11 | END 12 | python dev/convert_networks.py p2pool/networks.py p2pool/networks/ p2pool 13 | git add p2pool/networks/*.py 14 | git rm p2pool/networks.py 15 | 16 | mkdir -p p2pool/bitcoin/networks/ 17 | cat > p2pool/bitcoin/networks/__init__.py << END 18 | import pkgutil 19 | 20 | nets = dict((name, __import__(name, globals(), fromlist="dummy")) 21 | for module_loader, name, ispkg in pkgutil.iter_modules(__path__)) 22 | for net_name, net in nets.iteritems(): 23 | net.NAME = net_name 24 | END 25 | python dev/convert_networks.py p2pool/bitcoin/networks.py p2pool/bitcoin/networks/ bitcoin 26 | git add p2pool/bitcoin/networks/*.py 27 | git rm p2pool/bitcoin/networks.py 28 | -------------------------------------------------------------------------------- /dev/convert_networks.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | f = open(sys.argv[1]) 4 | 5 | while True: 6 | if f.readline().strip() == 'nets = dict(': break 7 | 8 | def nesting(l): 9 | res = 0 10 | for c in l: 11 | if c == '(': res += 1 12 | if c == ')': res -= 1 13 | return res 14 | 15 | def write_header(f, name): 16 | if sys.argv[3] == 'p2pool': 17 | f2.write('from p2pool.bitcoin import networks\n\n') 18 | if name == 'bitcoin': 19 | f2.write('''# CHAIN_LENGTH = number of shares back client keeps 20 | # REAL_CHAIN_LENGTH = maximum number of shares back client uses to compute payout 21 | # REAL_CHAIN_LENGTH must always be <= CHAIN_LENGTH 22 | # REAL_CHAIN_LENGTH must be changed in sync with all other clients 23 | # changes can be done by changing one, then the other 24 | 25 | ''') 26 | elif sys.argv[3] == 'bitcoin': 27 | f2.write('''import os 28 | import platform 29 | 30 | from twisted.internet import defer 31 | 32 | from .. import data, helper 33 | from p2pool.util import pack 34 | 35 | 36 | ''') 37 | else: assert False, 'invalid type argument' 38 | 39 | while True: 40 | l = f.readline() 41 | if not l.strip(): continue 42 | if l.strip() == ')': break 43 | 44 | name = l.strip().split('=')[0] 45 | 46 | lines = [] 47 | while True: 48 | l = f.readline() 49 | if not l.strip(): continue 50 | if l.strip() == '),': break 51 | while nesting(l) != 0: 52 | l += f.readline() 53 | lines.append(l.split('=', 1)) 54 | with open(sys.argv[2] + name + '.py', 'wb') as f2: 55 | write_header(f2, name) 56 | for a, b in lines: 57 | if ', #' in b: b = b.replace(', #', ' #') 58 | elif b.strip().endswith(','): b = b.strip()[:-1] 59 | else: assert False, b 60 | f2.write('%s = %s\n' % (a.strip(), b.strip())) 61 | -------------------------------------------------------------------------------- /dev/readme: -------------------------------------------------------------------------------- 1 | Release procedure: 2 | * Update network version 3 | * Tag+sign last commit 4 | * Upload Windows build 5 | * Make forum post 6 | * Update first post with links 7 | * Send email to mailing list if necessary 8 | -------------------------------------------------------------------------------- /fpconst.py: -------------------------------------------------------------------------------- 1 | """Utilities for handling IEEE 754 floating point special values 2 | 3 | This python module implements constants and functions for working with 4 | IEEE754 double-precision special values. It provides constants for 5 | Not-a-Number (NaN), Positive Infinity (PosInf), and Negative Infinity 6 | (NegInf), as well as functions to test for these values. 7 | 8 | The code is implemented in pure python by taking advantage of the 9 | 'struct' standard module. Care has been taken to generate proper 10 | results on both big-endian and little-endian machines. Some efficiency 11 | could be gained by translating the core routines into C. 12 | 13 | See 14 | for reference material on the IEEE 754 floating point standard. 15 | 16 | Further information on this package is available at 17 | . 18 | 19 | ------------------------------------------------------------------ 20 | Author: Gregory R. Warnes 21 | Date: 2005-02-24 22 | Version: 0.7.2 23 | Copyright: (c) 2003-2005 Pfizer, Licensed to PSF under a Contributor Agreement 24 | License: Licensed under the Apache License, Version 2.0 (the"License"); 25 | you may not use this file except in compliance with the License. 26 | You may obtain a copy of the License at 27 | 28 | http://www.apache.org/licenses/LICENSE-2.0 29 | 30 | Unless required by applicable law or agreed to in 31 | writing, software distributed under the License is 32 | distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 33 | CONDITIONS OF ANY KIND, either express or implied. See 34 | the License for the specific language governing 35 | permissions and limitations under the License. 36 | ------------------------------------------------------------------ 37 | """ 38 | 39 | __version__ = "0.7.2" 40 | ident = "$Id: fpconst.py,v 1.16 2005/02/24 17:42:03 warnes Exp $" 41 | 42 | import struct, operator 43 | 44 | # check endianess 45 | _big_endian = struct.pack('i',1)[0] != '\x01' 46 | 47 | # and define appropriate constants 48 | if(_big_endian): 49 | NaN = struct.unpack('d', '\x7F\xF8\x00\x00\x00\x00\x00\x00')[0] 50 | PosInf = struct.unpack('d', '\x7F\xF0\x00\x00\x00\x00\x00\x00')[0] 51 | NegInf = -PosInf 52 | else: 53 | NaN = struct.unpack('d', '\x00\x00\x00\x00\x00\x00\xf8\xff')[0] 54 | PosInf = struct.unpack('d', '\x00\x00\x00\x00\x00\x00\xf0\x7f')[0] 55 | NegInf = -PosInf 56 | 57 | def _double_as_bytes(dval): 58 | "Use struct.unpack to decode a double precision float into eight bytes" 59 | tmp = list(struct.unpack('8B',struct.pack('d', dval))) 60 | if not _big_endian: 61 | tmp.reverse() 62 | return tmp 63 | 64 | ## 65 | ## Functions to extract components of the IEEE 754 floating point format 66 | ## 67 | 68 | def _sign(dval): 69 | "Extract the sign bit from a double-precision floating point value" 70 | bb = _double_as_bytes(dval) 71 | return bb[0] >> 7 & 0x01 72 | 73 | def _exponent(dval): 74 | """Extract the exponentent bits from a double-precision floating 75 | point value. 76 | 77 | Note that for normalized values, the exponent bits have an offset 78 | of 1023. As a consequence, the actual exponentent is obtained 79 | by subtracting 1023 from the value returned by this function 80 | """ 81 | bb = _double_as_bytes(dval) 82 | return (bb[0] << 4 | bb[1] >> 4) & 0x7ff 83 | 84 | def _mantissa(dval): 85 | """Extract the _mantissa bits from a double-precision floating 86 | point value.""" 87 | 88 | bb = _double_as_bytes(dval) 89 | mantissa = bb[1] & 0x0f << 48 90 | mantissa += bb[2] << 40 91 | mantissa += bb[3] << 32 92 | mantissa += bb[4] 93 | return mantissa 94 | 95 | def _zero_mantissa(dval): 96 | """Determine whether the mantissa bits of the given double are all 97 | zero.""" 98 | bb = _double_as_bytes(dval) 99 | return ((bb[1] & 0x0f) | reduce(operator.or_, bb[2:])) == 0 100 | 101 | ## 102 | ## Functions to test for IEEE 754 special values 103 | ## 104 | 105 | def isNaN(value): 106 | "Determine if the argument is a IEEE 754 NaN (Not a Number) value." 107 | return (_exponent(value)==0x7ff and not _zero_mantissa(value)) 108 | 109 | def isInf(value): 110 | """Determine if the argument is an infinite IEEE 754 value (positive 111 | or negative inifinity)""" 112 | return (_exponent(value)==0x7ff and _zero_mantissa(value)) 113 | 114 | def isFinite(value): 115 | """Determine if the argument is an finite IEEE 754 value (i.e., is 116 | not NaN, positive or negative inifinity)""" 117 | return (_exponent(value)!=0x7ff) 118 | 119 | def isPosInf(value): 120 | "Determine if the argument is a IEEE 754 positive infinity value" 121 | return (_sign(value)==0 and _exponent(value)==0x7ff and \ 122 | _zero_mantissa(value)) 123 | 124 | def isNegInf(value): 125 | "Determine if the argument is a IEEE 754 negative infinity value" 126 | return (_sign(value)==1 and _exponent(value)==0x7ff and \ 127 | _zero_mantissa(value)) 128 | 129 | ## 130 | ## Functions to test public functions. 131 | ## 132 | 133 | def test_isNaN(): 134 | assert( not isNaN(PosInf) ) 135 | assert( not isNaN(NegInf) ) 136 | assert( isNaN(NaN ) ) 137 | assert( not isNaN( 1.0) ) 138 | assert( not isNaN( -1.0) ) 139 | 140 | def test_isInf(): 141 | assert( isInf(PosInf) ) 142 | assert( isInf(NegInf) ) 143 | assert( not isInf(NaN ) ) 144 | assert( not isInf( 1.0) ) 145 | assert( not isInf( -1.0) ) 146 | 147 | def test_isFinite(): 148 | assert( not isFinite(PosInf) ) 149 | assert( not isFinite(NegInf) ) 150 | assert( not isFinite(NaN ) ) 151 | assert( isFinite( 1.0) ) 152 | assert( isFinite( -1.0) ) 153 | 154 | def test_isPosInf(): 155 | assert( isPosInf(PosInf) ) 156 | assert( not isPosInf(NegInf) ) 157 | assert( not isPosInf(NaN ) ) 158 | assert( not isPosInf( 1.0) ) 159 | assert( not isPosInf( -1.0) ) 160 | 161 | def test_isNegInf(): 162 | assert( not isNegInf(PosInf) ) 163 | assert( isNegInf(NegInf) ) 164 | assert( not isNegInf(NaN ) ) 165 | assert( not isNegInf( 1.0) ) 166 | assert( not isNegInf( -1.0) ) 167 | 168 | # overall test 169 | def test(): 170 | test_isNaN() 171 | test_isInf() 172 | test_isFinite() 173 | test_isPosInf() 174 | test_isNegInf() 175 | 176 | if __name__ == "__main__": 177 | test() 178 | 179 | -------------------------------------------------------------------------------- /litecoin_scrypt/scrypt.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRYPT_H 2 | #define SCRYPT_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | void scrypt_1024_1_1_256(const char* input, char* output); 9 | void scrypt_1024_1_1_256_sp(const char* input, char* output, char* scratchpad); 10 | const int scrypt_scratchpad_size = 131583; 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /litecoin_scrypt/scryptmodule.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //#include "scrypt.h" 4 | 5 | static PyObject *scrypt_getpowhash(PyObject *self, PyObject *args) 6 | { 7 | char *output; 8 | PyObject *value; 9 | PyStringObject *input; 10 | if (!PyArg_ParseTuple(args, "S", &input)) 11 | return NULL; 12 | Py_INCREF(input); 13 | output = PyMem_Malloc(32); 14 | 15 | scrypt_1024_1_1_256((char *)PyString_AsString((PyObject*) input), output); 16 | Py_DECREF(input); 17 | value = Py_BuildValue("s#", output, 32); 18 | PyMem_Free(output); 19 | return value; 20 | } 21 | 22 | static PyMethodDef ScryptMethods[] = { 23 | { "getPoWHash", scrypt_getpowhash, METH_VARARGS, "Returns the proof of work hash using scrypt" }, 24 | { NULL, NULL, 0, NULL } 25 | }; 26 | 27 | PyMODINIT_FUNC initltc_scrypt(void) { 28 | (void) Py_InitModule("ltc_scrypt", ScryptMethods); 29 | } 30 | -------------------------------------------------------------------------------- /litecoin_scrypt/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | 3 | ltc_scrypt_module = Extension('ltc_scrypt', 4 | sources = ['scryptmodule.c', 5 | 'scrypt.c'], 6 | include_dirs=['.']) 7 | 8 | setup (name = 'ltc_scrypt', 9 | version = '1.0', 10 | description = 'Bindings for scrypt proof of work used by Litecoin', 11 | ext_modules = [ltc_scrypt_module]) 12 | -------------------------------------------------------------------------------- /nattraverso/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This package offers ways to retreive ip addresses of the machine, and map ports 3 | through various protocols. 4 | 5 | Currently only UPnP is implemented and available, in the pynupnp module. 6 | 7 | @author: Raphael Slinckx 8 | @copyright: Copyright 2005 9 | @license: LGPL 10 | @contact: U{raphael@slinckx.net} 11 | @version: 0.1.0 12 | """ 13 | 14 | __revision__ = "$id" 15 | __version__ = "0.1.0" 16 | -------------------------------------------------------------------------------- /nattraverso/ipdiscover.py: -------------------------------------------------------------------------------- 1 | """ 2 | Generic methods to retreive the IP address of the local machine. 3 | 4 | TODO: Example 5 | 6 | @author: Raphael Slinckx 7 | @copyright: Copyright 2005 8 | @license: LGPL 9 | @contact: U{raphael@slinckx.net} 10 | @version: 0.1.0 11 | """ 12 | 13 | __revision__ = "$id" 14 | 15 | import random, socket, logging, itertools 16 | 17 | from twisted.internet import defer, reactor 18 | 19 | from twisted.internet.protocol import DatagramProtocol 20 | from twisted.internet.error import CannotListenError 21 | 22 | from nattraverso.utils import is_rfc1918_ip, is_bogus_ip 23 | 24 | @defer.inlineCallbacks 25 | def get_local_ip(): 26 | """ 27 | Returns a deferred which will be called with a 28 | 2-uple (lan_flag, ip_address) : 29 | - lan_flag: 30 | - True if it's a local network (RFC1918) 31 | - False if it's a WAN address 32 | 33 | - ip_address is the actual ip address 34 | 35 | @return: A deferred called with the above defined tuple 36 | @rtype: L{twisted.internet.defer.Deferred} 37 | """ 38 | # first we try a connected udp socket, then via multicast 39 | logging.debug("Resolving dns to get udp ip") 40 | try: 41 | ipaddr = yield reactor.resolve('A.ROOT-SERVERS.NET') 42 | except: 43 | pass 44 | else: 45 | udpprot = DatagramProtocol() 46 | port = reactor.listenUDP(0, udpprot) 47 | udpprot.transport.connect(ipaddr, 7) 48 | localip = udpprot.transport.getHost().host 49 | port.stopListening() 50 | 51 | if is_bogus_ip(localip): 52 | raise RuntimeError, "Invalid IP address returned" 53 | else: 54 | defer.returnValue((is_rfc1918_ip(localip), localip)) 55 | 56 | logging.debug("Multicast ping to retrieve local IP") 57 | ipaddr = yield _discover_multicast() 58 | defer.returnValue((is_rfc1918_ip(ipaddr), ipaddr)) 59 | 60 | @defer.inlineCallbacks 61 | def get_external_ip(): 62 | """ 63 | Returns a deferred which will be called with a 64 | 2-uple (wan_flag, ip_address): 65 | - wan_flag: 66 | - True if it's a WAN address 67 | - False if it's a LAN address 68 | - None if it's a localhost (127.0.0.1) address 69 | - ip_address: the most accessible ip address of this machine 70 | 71 | @return: A deferred called with the above defined tuple 72 | @rtype: L{twisted.internet.defer.Deferred} 73 | """ 74 | 75 | try: 76 | local, ipaddr = yield get_local_ip() 77 | except: 78 | defer.returnValue((None, "127.0.0.1")) 79 | if not local: 80 | defer.returnValue((True, ipaddr)) 81 | logging.debug("Got local ip, trying to use upnp to get WAN ip") 82 | import nattraverso.pynupnp 83 | try: 84 | ipaddr2 = yield nattraverso.pynupnp.get_external_ip() 85 | except: 86 | defer.returnValue((False, ipaddr)) 87 | else: 88 | defer.returnValue((True, ipaddr2)) 89 | 90 | class _LocalNetworkMulticast(DatagramProtocol): 91 | def __init__(self, nonce): 92 | from p2pool.util import variable 93 | 94 | self.nonce = nonce 95 | self.address_received = variable.Event() 96 | 97 | def datagramReceived(self, dgram, addr): 98 | """Datagram received, we callback the IP address.""" 99 | logging.debug("Received multicast pong: %s; addr:%r", dgram, addr) 100 | if dgram != self.nonce: 101 | return 102 | self.address_received.happened(addr[0]) 103 | 104 | @defer.inlineCallbacks 105 | def _discover_multicast(): 106 | """ 107 | Local IP discovery protocol via multicast: 108 | - Broadcast 3 ping multicast packet with "ping" in it 109 | - Wait for an answer 110 | - Retrieve the ip address from the returning packet, which is ours 111 | """ 112 | 113 | nonce = str(random.randrange(2**64)) 114 | p = _LocalNetworkMulticast(nonce) 115 | 116 | for attempt in itertools.count(): 117 | port = 11000 + random.randint(0, 5000) 118 | try: 119 | mcast = reactor.listenMulticast(port, p) 120 | except CannotListenError: 121 | if attempt >= 10: 122 | raise 123 | continue 124 | else: 125 | break 126 | 127 | try: 128 | yield mcast.joinGroup('239.255.255.250', socket.INADDR_ANY) 129 | 130 | logging.debug("Sending multicast ping") 131 | for i in xrange(3): 132 | p.transport.write(nonce, ('239.255.255.250', port)) 133 | 134 | address, = yield p.address_received.get_deferred(5) 135 | finally: 136 | mcast.stopListening() 137 | 138 | defer.returnValue(address) 139 | -------------------------------------------------------------------------------- /nattraverso/portmapper.py: -------------------------------------------------------------------------------- 1 | """ 2 | Generic NAT Port mapping interface. 3 | 4 | TODO: Example 5 | 6 | @author: Raphael Slinckx 7 | @copyright: Copyright 2005 8 | @license: LGPL 9 | @contact: U{raphael@slinckx.net} 10 | @version: 0.1.0 11 | """ 12 | 13 | __revision__ = "$id" 14 | 15 | from twisted.internet.base import BasePort 16 | 17 | # Public API 18 | def get_port_mapper(proto="TCP"): 19 | """ 20 | Returns a L{NATMapper} instance, suited to map a port for 21 | the given protocol. Defaults to TCP. 22 | 23 | For the moment, only upnp mapper is available. It accepts both UDP and TCP. 24 | 25 | @param proto: The protocol: 'TCP' or 'UDP' 26 | @type proto: string 27 | @return: A deferred called with a L{NATMapper} instance 28 | @rtype: L{twisted.internet.defer.Deferred} 29 | """ 30 | import nattraverso.pynupnp 31 | return nattraverso.pynupnp.get_port_mapper() 32 | 33 | class NATMapper: 34 | """ 35 | Define methods to map port objects (as returned by twisted's listenXX). 36 | This allows NAT to be traversed from incoming packets. 37 | 38 | Currently the only implementation of this class is the UPnP Mapper, which 39 | can map UDP and TCP ports, if an UPnP Device exists. 40 | """ 41 | def __init__(self): 42 | raise NotImplementedError("Cannot instantiate the class") 43 | 44 | def map(self, port): 45 | """ 46 | Create a mapping for the given twisted's port object. 47 | 48 | The deferred will call back with a tuple (extaddr, extport): 49 | - extaddr: The ip string of the external ip address of this host 50 | - extport: the external port number used to map the given Port object 51 | 52 | When called multiple times with the same Port, 53 | callback with the existing mapping. 54 | 55 | @param port: The port object to map 56 | @type port: a L{twisted.internet.interfaces.IListeningPort} object 57 | @return: A deferred called with the above defined tuple 58 | @rtype: L{twisted.internet.defer.Deferred} 59 | """ 60 | raise NotImplementedError 61 | 62 | def info(self, port): 63 | """ 64 | Returns the existing mapping for the given port object. That means map() 65 | has to be called before. 66 | 67 | @param port: The port object to retreive info from 68 | @type port: a L{twisted.internet.interfaces.IListeningPort} object 69 | @raise ValueError: When there is no such existing mapping 70 | @return: a tuple (extaddress, extport). 71 | @see: L{map() function} 72 | """ 73 | raise NotImplementedError 74 | 75 | def unmap(self, port): 76 | """ 77 | Remove an existing mapping for the given twisted's port object. 78 | 79 | @param port: The port object to unmap 80 | @type port: a L{twisted.internet.interfaces.IListeningPort} object 81 | @return: A deferred called with None 82 | @rtype: L{twisted.internet.defer.Deferred} 83 | @raise ValueError: When there is no such existing mapping 84 | """ 85 | raise NotImplementedError 86 | 87 | def get_port_mappings(self): 88 | """ 89 | Returns a deferred that will be called with a dictionnary of the 90 | existing mappings. 91 | 92 | The dictionnary structure is the following: 93 | - Keys: tuple (protocol, external_port) 94 | - protocol is "TCP" or "UDP". 95 | - external_port is the external port number, as see on the 96 | WAN side. 97 | - Values:tuple (internal_ip, internal_port) 98 | - internal_ip is the LAN ip address of the host. 99 | - internal_port is the internal port number mapped 100 | to external_port. 101 | 102 | @return: A deferred called with the above defined dictionnary 103 | @rtype: L{twisted.internet.defer.Deferred} 104 | """ 105 | raise NotImplementedError 106 | 107 | def _check_valid_port(self, port): 108 | """Various Port object validity checks. Raise a ValueError.""" 109 | if not isinstance(port, BasePort): 110 | raise ValueError("expected a Port, got %r"%(port)) 111 | 112 | if not port.connected: 113 | raise ValueError("Port %r is not listening"%(port)) 114 | 115 | loc_addr = port.getHost() 116 | if loc_addr.port == 0: 117 | raise ValueError("Port %r has port number of 0"%(port)) 118 | 119 | -------------------------------------------------------------------------------- /nattraverso/pynupnp/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This package offers ways to retreive ip addresses of the machine, and map ports 3 | through UPnP devices. 4 | 5 | @author: Raphael Slinckx 6 | @copyright: Copyright 2005 7 | @license: LGPL 8 | @contact: U{raphael@slinckx.net} 9 | @version: 0.1.0 10 | """ 11 | __revision__ = "$id" 12 | 13 | from nattraverso.pynupnp.upnp import search_upnp_device, UPnPMapper 14 | 15 | def get_external_ip(): 16 | """ 17 | Returns a deferred which will be called with the WAN ip address 18 | retreived through UPnP. The ip is a string of the form "x.x.x.x" 19 | 20 | @return: A deferred called with the external ip address of this host 21 | @rtype: L{twisted.internet.defer.Deferred} 22 | """ 23 | return search_upnp_device().addCallback(lambda x: x.get_external_ip()) 24 | 25 | def get_port_mapper(): 26 | """ 27 | Returns a deferred which will be called with a L{UPnPMapper} instance. 28 | This is a L{nattraverso.portmapper.NATMapper} implementation. 29 | 30 | @return: A deferred called with the L{UPnPMapper} instance. 31 | @rtype: L{twisted.internet.defer.Deferred} 32 | """ 33 | return search_upnp_device().addCallback(lambda x: UPnPMapper(x)) 34 | -------------------------------------------------------------------------------- /nattraverso/pynupnp/soap.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module is a SOAP client using twisted's deferreds. 3 | It uses the SOAPpy package. 4 | 5 | @author: Raphael Slinckx 6 | @copyright: Copyright 2005 7 | @license: LGPL 8 | @contact: U{raphael@slinckx.net} 9 | @version: 0.1.0 10 | """ 11 | 12 | __revision__ = "$id" 13 | 14 | import SOAPpy, logging 15 | from SOAPpy.Config import Config 16 | from twisted.web import client, error 17 | 18 | #General config 19 | Config.typed = False 20 | 21 | class SoapError(Exception): 22 | """ 23 | This is a SOAP error message, not an HTTP error message. 24 | 25 | The content of this error is a SOAPpy structure representing the 26 | SOAP error message. 27 | """ 28 | pass 29 | 30 | class SoapProxy: 31 | """ 32 | Proxy for an url to which we send SOAP rpc calls. 33 | """ 34 | def __init__(self, url, prefix): 35 | """ 36 | Init the proxy, it will connect to the given url, using the 37 | given soap namespace. 38 | 39 | @param url: The url of the remote host to call 40 | @param prefix: The namespace prefix to use, eg. 41 | 'urn:schemas-upnp-org:service:WANIPConnection:1' 42 | """ 43 | logging.debug("Soap Proxy: '%s', prefix: '%s'", url, prefix) 44 | self._url = url 45 | self._prefix = prefix 46 | 47 | def call(self, method, **kwargs): 48 | """ 49 | Call the given remote method with the given arguments, as keywords. 50 | 51 | Returns a deferred, called with SOAPpy structure representing 52 | the soap response. 53 | 54 | @param method: The method name to call, eg. 'GetExternalIP' 55 | @param kwargs: The parameters of the call, as keywords 56 | @return: A deferred called with the external ip address of this host 57 | @rtype: L{twisted.internet.defer.Deferred} 58 | """ 59 | payload = SOAPpy.buildSOAP(method=method, config=Config, namespace=self._prefix, kw=kwargs) 60 | # Here begins the nasty hack 61 | payload = payload.replace( 62 | # Upnp wants s: instead of SOAP-ENV 63 | 'SOAP-ENV','s').replace( 64 | # Doesn't seem to like these encoding stuff 65 | 'xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"', '').replace( 66 | 'SOAP-ENC:root="1"', '').replace( 67 | # And it wants u: instead of ns1 namespace for arguments.. 68 | 'ns1','u') 69 | 70 | logging.debug("SOAP Payload:\n%s", payload) 71 | 72 | return client.getPage(self._url, postdata=payload, method="POST", 73 | headers={'content-type': 'text/xml', 'SOAPACTION': '%s#%s' % (self._prefix, method)} 74 | ).addCallbacks(self._got_page, self._got_error) 75 | 76 | def _got_page(self, result): 77 | """ 78 | The http POST command was successful, we parse the SOAP 79 | answer, and return it. 80 | 81 | @param result: the xml content 82 | """ 83 | parsed = SOAPpy.parseSOAPRPC(result) 84 | 85 | logging.debug("SOAP Answer:\n%s", result) 86 | logging.debug("SOAP Parsed Answer: %r", parsed) 87 | 88 | return parsed 89 | 90 | def _got_error(self, res): 91 | """ 92 | The HTTP POST command did not succeed, depending on the error type: 93 | - it's a SOAP error, we parse it and return a L{SoapError}. 94 | - it's another type of error (http, other), we raise it as is 95 | """ 96 | logging.debug("SOAP Error:\n%s", res) 97 | 98 | if isinstance(res.value, error.Error): 99 | try: 100 | logging.debug("SOAP Error content:\n%s", res.value.response) 101 | raise SoapError(SOAPpy.parseSOAPRPC(res.value.response)["detail"]) 102 | except: 103 | raise 104 | raise Exception(res.value) 105 | -------------------------------------------------------------------------------- /nattraverso/pynupnp/upnpxml.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module parse an UPnP device's XML definition in an Object. 3 | 4 | @author: Raphael Slinckx 5 | @copyright: Copyright 2005 6 | @license: LGPL 7 | @contact: U{raphael@slinckx.net} 8 | @version: 0.1.0 9 | """ 10 | 11 | __revision__ = "$id" 12 | 13 | from xml.dom import minidom 14 | import logging 15 | 16 | # Allowed UPnP services to use when mapping ports/external addresses 17 | WANSERVICES = ['urn:schemas-upnp-org:service:WANIPConnection:1', 18 | 'urn:schemas-upnp-org:service:WANPPPConnection:1'] 19 | 20 | class UPnPXml: 21 | """ 22 | This objects parses the XML definition, and stores the useful 23 | results in attributes. 24 | 25 | The device infos dictionnary may contain the following keys: 26 | - friendlyname: A friendly name to call the device. 27 | - manufacturer: A manufacturer name for the device. 28 | 29 | Here are the different attributes: 30 | - deviceinfos: A dictionnary of device infos as defined above. 31 | - controlurl: The control url, this is the url to use when sending SOAP 32 | requests to the device, relative to the base url. 33 | - wanservice: The WAN service to be used, one of the L{WANSERVICES} 34 | - urlbase: The base url to use when talking in SOAP to the device. 35 | 36 | The full url to use is obtained by urljoin(urlbase, controlurl) 37 | """ 38 | 39 | def __init__(self, xml): 40 | """ 41 | Parse the given XML string for UPnP infos. This creates the attributes 42 | when they are found, or None if no value was found. 43 | 44 | @param xml: a xml string to parse 45 | """ 46 | logging.debug("Got UPNP Xml description:\n%s", xml) 47 | doc = minidom.parseString(xml) 48 | 49 | # Fetch various device info 50 | self.deviceinfos = {} 51 | try: 52 | attributes = { 53 | 'friendlyname':'friendlyName', 54 | 'manufacturer' : 'manufacturer' 55 | } 56 | device = doc.getElementsByTagName('device')[0] 57 | for name, tag in attributes.iteritems(): 58 | try: 59 | self.deviceinfos[name] = device.getElementsByTagName( 60 | tag)[0].firstChild.datas.encode('utf-8') 61 | except: 62 | pass 63 | except: 64 | pass 65 | 66 | # Fetch device control url 67 | self.controlurl = None 68 | self.wanservice = None 69 | 70 | for service in doc.getElementsByTagName('service'): 71 | try: 72 | stype = service.getElementsByTagName( 73 | 'serviceType')[0].firstChild.data.encode('utf-8') 74 | if stype in WANSERVICES: 75 | self.controlurl = service.getElementsByTagName( 76 | 'controlURL')[0].firstChild.data.encode('utf-8') 77 | self.wanservice = stype 78 | break 79 | except: 80 | pass 81 | 82 | # Find base url 83 | self.urlbase = None 84 | try: 85 | self.urlbase = doc.getElementsByTagName( 86 | 'URLBase')[0].firstChild.data.encode('utf-8') 87 | except: 88 | pass 89 | 90 | -------------------------------------------------------------------------------- /nattraverso/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Various utility functions used in the nattraverso package. 3 | 4 | @author: Raphael Slinckx 5 | @copyright: Copyright 2005 6 | @license: LGPL 7 | @contact: U{raphael@slinckx.net} 8 | @version: 0.1.0 9 | """ 10 | __revision__ = "$id" 11 | 12 | def is_rfc1918_ip(ip): 13 | """ 14 | Checks if the given ip address is a rfc1918 one. 15 | 16 | @param ip: The ip address to test 17 | @type ip: a string "x.x.x.x" 18 | @return: True if it's a LAN address, False otherwise 19 | """ 20 | if isinstance(ip, basestring): 21 | ip = _ip_to_number(ip) 22 | 23 | for net, mask in _nets: 24 | if ip&mask == net: 25 | return True 26 | 27 | return False 28 | 29 | def is_bogus_ip(ip): 30 | """ 31 | Checks if the given ip address is bogus, i.e. 0.0.0.0 or 127.0.0.1. 32 | 33 | @param ip: The ip address to test 34 | @type ip: a string "x.x.x.x" 35 | @return: True if it's bogus, False otherwise 36 | """ 37 | return ip.startswith('0.') or ip.startswith('127.') 38 | 39 | def _ip_to_number(ipstr): 40 | """ 41 | Translate a string ip address to a packed number. 42 | 43 | @param ipstr: the ip address to transform 44 | @type ipstr: a string "x.x.x.x" 45 | @return: an int32 number representing the ip address 46 | """ 47 | net = [ int(digit) for digit in ipstr.split('.') ] + [ 0, 0, 0 ] 48 | net = net[:4] 49 | return ((((((0L+net[0])<<8) + net[1])<<8) + net[2])<<8) +net[3] 50 | 51 | # List of rfc1918 net/mask 52 | _rfc1918_networks = [('127', 8), ('192.168', 16), ('10', 8), ('172.16', 12)] 53 | # Machine readable form of the above 54 | _nets = [(_ip_to_number(net), (2L**32 -1)^(2L**(32-mask)-1)) 55 | for net, mask in _rfc1918_networks] 56 | 57 | -------------------------------------------------------------------------------- /p2pool/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | import traceback 5 | import subprocess 6 | 7 | def check_output(*popenargs, **kwargs): 8 | process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) 9 | output, unused_err = process.communicate() 10 | retcode = process.poll() 11 | if retcode: 12 | raise ValueError((retcode, output)) 13 | return output 14 | 15 | def _get_version(): 16 | try: 17 | try: 18 | return check_output(['git', 'describe', '--always', '--dirty'], cwd=os.path.dirname(os.path.abspath(sys.argv[0]))).strip() 19 | except: 20 | pass 21 | try: 22 | return check_output(['git.cmd', 'describe', '--always', '--dirty'], cwd=os.path.dirname(os.path.abspath(sys.argv[0]))).strip() 23 | except: 24 | pass 25 | 26 | root_dir = os.path.abspath(os.path.dirname(sys.argv[0])) 27 | git_dir = os.path.join(root_dir, '.git') 28 | if os.path.exists(git_dir): 29 | head = open(os.path.join(git_dir, 'HEAD')).read().strip() 30 | prefix = 'ref: ' 31 | if head.startswith(prefix): 32 | path = head[len(prefix):].split('/') 33 | return open(os.path.join(git_dir, *path)).read().strip()[:7] 34 | else: 35 | return head[:7] 36 | 37 | dir_name = os.path.split(root_dir)[1] 38 | match = re.match('p2pool-([.0-9]+)', dir_name) 39 | if match: 40 | return match.groups()[0] 41 | 42 | return 'unknown %s' % (dir_name.encode('hex'),) 43 | except Exception, e: 44 | traceback.print_exc() 45 | return 'unknown %s' % (str(e).encode('hex'),) 46 | 47 | __version__ = _get_version() 48 | 49 | DEBUG = True 50 | -------------------------------------------------------------------------------- /p2pool/bitcoin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtoomim/p2pool/ece15b03fc2367d02e730b2b85b666217fa8985e/p2pool/bitcoin/__init__.py -------------------------------------------------------------------------------- /p2pool/bitcoin/getwork.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Representation of a getwork request/reply 3 | ''' 4 | 5 | from __future__ import division 6 | 7 | from . import data as bitcoin_data 8 | from . import sha256 9 | from p2pool.util import pack 10 | 11 | def _swap4(s): 12 | if len(s) % 4: 13 | raise ValueError() 14 | return ''.join(s[x:x+4][::-1] for x in xrange(0, len(s), 4)) 15 | 16 | class BlockAttempt(object): 17 | def __init__(self, version, previous_block, merkle_root, timestamp, bits, share_target): 18 | self.version, self.previous_block, self.merkle_root, self.timestamp, self.bits, self.share_target = version, previous_block, merkle_root, timestamp, bits, share_target 19 | 20 | def __hash__(self): 21 | return hash((self.version, self.previous_block, self.merkle_root, self.timestamp, self.bits, self.share_target)) 22 | 23 | def __eq__(self, other): 24 | if not isinstance(other, BlockAttempt): 25 | raise ValueError('comparisons only valid with other BlockAttempts') 26 | return self.__dict__ == other.__dict__ 27 | 28 | def __ne__(self, other): 29 | return not (self == other) 30 | 31 | def __repr__(self): 32 | return 'BlockAttempt(%s)' % (', '.join('%s=%r' % (k, v) for k, v in self.__dict__.iteritems()),) 33 | 34 | def getwork(self, **extra): 35 | if 'data' in extra or 'hash1' in extra or 'target' in extra or 'midstate' in extra: 36 | raise ValueError() 37 | 38 | block_data = bitcoin_data.block_header_type.pack(dict( 39 | version=self.version, 40 | previous_block=self.previous_block, 41 | merkle_root=self.merkle_root, 42 | timestamp=self.timestamp, 43 | bits=self.bits, 44 | nonce=0, 45 | )) 46 | 47 | getwork = { 48 | 'data': _swap4(block_data).encode('hex') + '000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000', 49 | 'hash1': '00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000', 50 | 'target': pack.IntType(256).pack(self.share_target).encode('hex'), 51 | 'midstate': _swap4(sha256.process(sha256.initial_state, block_data[:64])).encode('hex'), 52 | } 53 | 54 | getwork = dict(getwork) 55 | getwork.update(extra) 56 | 57 | return getwork 58 | 59 | @classmethod 60 | def from_getwork(cls, getwork): 61 | attrs = decode_data(getwork['data']) 62 | 63 | return cls( 64 | version=attrs['version'], 65 | previous_block=attrs['previous_block'], 66 | merkle_root=attrs['merkle_root'], 67 | timestamp=attrs['timestamp'], 68 | bits=attrs['bits'], 69 | share_target=pack.IntType(256).unpack(getwork['target'].decode('hex')), 70 | ) 71 | 72 | def update(self, **kwargs): 73 | d = self.__dict__.copy() 74 | d.update(kwargs) 75 | return self.__class__(**d) 76 | 77 | def decode_data(data): 78 | return bitcoin_data.block_header_type.unpack(_swap4(data.decode('hex'))[:80]) 79 | -------------------------------------------------------------------------------- /p2pool/bitcoin/height_tracker.py: -------------------------------------------------------------------------------- 1 | from twisted.internet import defer 2 | from twisted.python import log 3 | 4 | import p2pool 5 | from p2pool.bitcoin import data as bitcoin_data 6 | from p2pool.util import deferral, forest, jsonrpc, variable 7 | 8 | class HeaderWrapper(object): 9 | __slots__ = 'hash previous_hash'.split(' ') 10 | 11 | @classmethod 12 | def from_header(cls, header): 13 | return cls(bitcoin_data.hash256(bitcoin_data.block_header_type.pack(header)), header['previous_block']) 14 | 15 | def __init__(self, hash, previous_hash): 16 | self.hash, self.previous_hash = hash, previous_hash 17 | 18 | class HeightTracker(object): 19 | '''Point this at a factory and let it take care of getting block heights''' 20 | 21 | def __init__(self, best_block_func, factory, backlog_needed): 22 | self._best_block_func = best_block_func 23 | self._factory = factory 24 | self._backlog_needed = backlog_needed 25 | 26 | self._tracker = forest.Tracker() 27 | 28 | self._watch1 = self._factory.new_headers.watch(self._heard_headers) 29 | self._watch2 = self._factory.new_block.watch(self._request) 30 | 31 | self._requested = set() 32 | self._clear_task = deferral.RobustLoopingCall(self._requested.clear) 33 | self._clear_task.start(60) 34 | 35 | self._last_notified_size = 0 36 | 37 | self.updated = variable.Event() 38 | 39 | self._think_task = deferral.RobustLoopingCall(self._think) 40 | self._think_task.start(15) 41 | self._think2_task = deferral.RobustLoopingCall(self._think2) 42 | self._think2_task.start(15) 43 | 44 | def _think(self): 45 | try: 46 | highest_head = max(self._tracker.heads, key=lambda h: self._tracker.get_height_and_last(h)[0]) if self._tracker.heads else None 47 | if highest_head is None: 48 | return # wait for think2 49 | height, last = self._tracker.get_height_and_last(highest_head) 50 | if height < self._backlog_needed: 51 | self._request(last) 52 | except: 53 | log.err(None, 'Error in HeightTracker._think:') 54 | 55 | def _think2(self): 56 | self._request(self._best_block_func()) 57 | 58 | def _heard_headers(self, headers): 59 | changed = False 60 | for header in headers: 61 | hw = HeaderWrapper.from_header(header) 62 | if hw.hash in self._tracker.items: 63 | continue 64 | changed = True 65 | self._tracker.add(hw) 66 | if changed: 67 | self.updated.happened() 68 | self._think() 69 | 70 | if len(self._tracker.items) >= self._last_notified_size + 100: 71 | print 'Have %i/%i block headers' % (len(self._tracker.items), self._backlog_needed) 72 | self._last_notified_size = len(self._tracker.items) 73 | 74 | @defer.inlineCallbacks 75 | def _request(self, last): 76 | if last in self._tracker.items: 77 | return 78 | if last in self._requested: 79 | return 80 | self._requested.add(last) 81 | (yield self._factory.getProtocol()).send_getheaders(version=1, have=[], last=last) 82 | 83 | def get_height_rel_highest(self, block_hash): 84 | # callers: highest height can change during yields! 85 | best_height, best_last = self._tracker.get_height_and_last(self._best_block_func()) 86 | height, last = self._tracker.get_height_and_last(block_hash) 87 | if last != best_last: 88 | return -1000000000 # XXX hack 89 | return height - best_height 90 | def get_height(self, block_hash): 91 | # callers: highest height can change during yields! 92 | height, last = self._tracker.get_height_and_last(block_hash) 93 | return height 94 | 95 | @defer.inlineCallbacks 96 | def get_height_funcs(bitcoind, factory, best_block_func, net): 97 | cached_heights = {} 98 | if '\ngetblockheader ' in (yield deferral.retry()(bitcoind.rpc_help)()): 99 | @deferral.DeferredCacher 100 | @defer.inlineCallbacks 101 | def height_cacher(block_hash): 102 | if not block_hash in cached_heights: 103 | try: 104 | x = yield bitcoind.rpc_getblockheader('%064x' % (block_hash,)) 105 | except jsonrpc.Error_for_code(-5): # Block not found 106 | if not p2pool.DEBUG: 107 | raise deferral.RetrySilentlyException() 108 | else: 109 | raise 110 | cached_heights[block_hash] = x['blockcount'] if 'blockcount' in x else x['height'] 111 | defer.returnValue(cached_heights[block_hash]) 112 | best_height_cached = variable.Variable((yield deferral.retry()(height_cacher)(best_block_func()))) 113 | def get_height_rel_highest(block_hash): 114 | this_height = height_cacher.call_now(block_hash, 0) 115 | best_height = height_cacher.call_now(best_block_func(), 0) 116 | best_height_cached.set(max(best_height_cached.value, this_height, best_height)) 117 | return this_height - best_height_cached.value 118 | def get_height(block_hash): 119 | this_height = height_cacher.call_now(block_hash, 0) 120 | return this_height 121 | else: 122 | get_height_rel_highest = HeightTracker(best_block_func, factory, 5*net.SHARE_PERIOD*net.CHAIN_LENGTH/net.PARENT.BLOCK_PERIOD).get_height_rel_highest 123 | get_height = HeightTracker(best_block_func, factory, 5*net.SHARE_PERIOD*net.CHAIN_LENGTH/net.PARENT.BLOCK_PERIOD).get_height 124 | defer.returnValue((get_height_rel_highest, get_height)) 125 | 126 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/__init__.py: -------------------------------------------------------------------------------- 1 | import pkgutil 2 | 3 | nets = dict((name, __import__(name, globals(), fromlist="dummy")) 4 | for module_loader, name, ispkg in pkgutil.iter_modules(__path__)) 5 | for net_name, net in nets.iteritems(): 6 | net.NAME = net_name 7 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/bitcoin.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | from twisted.internet import defer 5 | 6 | from .. import data, helper 7 | from p2pool.util import pack 8 | 9 | 10 | P2P_PREFIX = 'f9beb4d9'.decode('hex') 11 | P2P_PORT = 8333 12 | ADDRESS_VERSION = 0 13 | ADDRESS_P2SH_VERSION = 5 14 | HUMAN_READABLE_PART = 'bc' 15 | RPC_PORT = 8332 16 | RPC_CHECK = defer.inlineCallbacks(lambda bitcoind: defer.returnValue( 17 | (yield helper.check_block_header(bitcoind, '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f')) and # genesis block 18 | (yield helper.check_block_header(bitcoind, '00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148')) and # 478559 -- Bitcoin Cash fork 19 | (yield helper.check_block_header(bitcoind, '0000000000000000002ac644c9ba8ac3be966276fb7fc8f3baa1a3b9bdc615f1')) and # 491408 -- Bitcoin Gold fork 20 | (yield bitcoind.rpc_getblockchaininfo())['chain'] == 'main' 21 | )) 22 | SUBSIDY_FUNC = lambda height: 50*100000000 >> (height + 1)//210000 23 | POW_FUNC = data.hash256 24 | BLOCK_PERIOD = 600 # s 25 | SYMBOL = 'BTC' 26 | CONF_FILE_FUNC = lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Bitcoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Bitcoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.bitcoin'), 'bitcoin.conf') 27 | BLOCK_EXPLORER_URL_PREFIX = 'https://blockchain.info/block/' 28 | ADDRESS_EXPLORER_URL_PREFIX = 'https://blockchain.info/address/' 29 | TX_EXPLORER_URL_PREFIX = 'https://blockchain.info/tx/' 30 | SANE_TARGET_RANGE = (2**256//2**32//1000000 - 1, 2**256//2**32 - 1) 31 | DUMB_SCRYPT_DIFF = 1 32 | DUST_THRESHOLD = 0.001e8 33 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/bitcoin_testnet.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | from twisted.internet import defer 5 | 6 | from .. import data, helper 7 | from p2pool.util import pack 8 | 9 | 10 | P2P_PREFIX = '0b110907'.decode('hex') 11 | P2P_PORT = 18333 12 | ADDRESS_VERSION = 111 13 | ADDRESS_P2SH_VERSION = 196 14 | HUMAN_READABLE_PART = 'tb' 15 | RPC_PORT = 18332 16 | RPC_CHECK = defer.inlineCallbacks(lambda bitcoind: defer.returnValue( 17 | 'getreceivedbyaddress' in (yield bitcoind.rpc_help()) and 18 | (yield bitcoind.rpc_getblockchaininfo())['chain'] == 'test' 19 | )) 20 | SUBSIDY_FUNC = lambda height: 50*100000000 >> (height + 1)//210000 21 | POW_FUNC = data.hash256 22 | BLOCK_PERIOD = 600 # s 23 | SYMBOL = 'tBTC' 24 | CONF_FILE_FUNC = lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Bitcoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Bitcoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.bitcoin'), 'bitcoin.conf') 25 | BLOCK_EXPLORER_URL_PREFIX = 'http://blockexplorer.com/testnet/block/' 26 | ADDRESS_EXPLORER_URL_PREFIX = 'http://blockexplorer.com/testnet/address/' 27 | TX_EXPLORER_URL_PREFIX = 'http://blockexplorer.com/testnet/tx/' 28 | SANE_TARGET_RANGE = (2**256//2**32//100000000 - 1, 2**256//2**32 - 1) 29 | DUMB_SCRYPT_DIFF = 1 30 | DUST_THRESHOLD = 1e8 31 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/bitcoincash.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | from twisted.internet import defer 5 | 6 | from .. import data, helper 7 | from p2pool.util import pack 8 | 9 | 10 | #P2P_PREFIX = 'f9beb4d9'.decode('hex') # disk magic and old net magic 11 | P2P_PREFIX = 'e3e1f3e8'.decode('hex') # new net magic 12 | P2P_PORT = 8333 13 | ADDRESS_VERSION = 0 14 | ADDRESS_P2SH_VERSION = 5 15 | HUMAN_READABLE_PART = 'bitcoincash' 16 | RPC_PORT = 8332 17 | RPC_CHECK = defer.inlineCallbacks(lambda bitcoind: defer.returnValue( 18 | (yield helper.check_block_header(bitcoind, '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f')) and # genesis block 19 | (yield helper.check_block_header(bitcoind, '000000000000000000651ef99cb9fcbe0dadde1d424bd9f15ff20136191a5eec')) and # 478559 -- Bitcoin Cash fork 20 | (yield bitcoind.rpc_getblockchaininfo())['chain'] == 'main' 21 | )) 22 | SUBSIDY_FUNC = lambda height: 50*100000000 >> (height + 1)//210000 23 | POW_FUNC = data.hash256 24 | BLOCK_PERIOD = 600 # s 25 | SYMBOL = 'BCH' 26 | CONF_FILE_FUNC = lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Bitcoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Bitcoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.bitcoin'), 'bitcoin.conf') 27 | BLOCK_EXPLORER_URL_PREFIX = 'https://explorer.bitcoin.com/bch/block/' 28 | ADDRESS_EXPLORER_URL_PREFIX = 'https://explorer.bitcoin.com/bch/address/' 29 | TX_EXPLORER_URL_PREFIX = 'https://explorer.bitcoin.com/bch/tx/' 30 | SANE_TARGET_RANGE = (2**256//2**32//100000000 - 1, 2**256//2**32 - 1) 31 | DUMB_SCRYPT_DIFF = 1 32 | DUST_THRESHOLD = 0.001e8 33 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/bitcoincash_testnet.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | from twisted.internet import defer 5 | 6 | from .. import data, helper 7 | from p2pool.util import pack 8 | 9 | 10 | #P2P_PREFIX = '0b110907'.decode('hex') # disk magic and old netmagic 11 | P2P_PREFIX = 'f4e5f3f4'.decode('hex') # new net magic 12 | P2P_PORT = 18333 13 | ADDRESS_VERSION = 111 14 | ADDRESS_P2SH_VERSION = 196 15 | HUMAN_READABLE_PART = 'bchtest' 16 | RPC_PORT = 18332 17 | RPC_CHECK = defer.inlineCallbacks(lambda bitcoind: defer.returnValue( 18 | 'getreceivedbyaddress' in (yield bitcoind.rpc_help()) and 19 | (yield bitcoind.rpc_getblockchaininfo())['chain'] == 'test' 20 | )) 21 | SUBSIDY_FUNC = lambda height: 50*100000000 >> (height + 1)//210000 22 | POW_FUNC = data.hash256 23 | BLOCK_PERIOD = 600 # s 24 | SYMBOL = 'tBCH' 25 | CONF_FILE_FUNC = lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Bitcoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Bitcoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.bitcoin'), 'bitcoin.conf') 26 | BLOCK_EXPLORER_URL_PREFIX = 'https://explorer.bitcoin.com/tbch/block/' 27 | ADDRESS_EXPLORER_URL_PREFIX = 'https://explorer.bitcoin.com/tbch/address/' 28 | TX_EXPLORER_URL_PREFIX = 'https://www.blocktrail.com/tBCC/tx/' 29 | SANE_TARGET_RANGE = (2**256//2**32//100000000 - 1, 2**256//2**32 - 1) 30 | BLOCK_EXPLORER_URL_PREFIX = 'https://explorer.bitcoin.com/tbch/block/' 31 | ADDRESS_EXPLORER_URL_PREFIX = 'https://explorer.bitcoin.com/tbch/address/' 32 | TX_EXPLORER_URL_PREFIX = 'https://explorer.bitcoin.com/tbch/tx/' 33 | DUMB_SCRYPT_DIFF = 1 34 | DUST_THRESHOLD = 1e8 35 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/bitcoinsv.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | from twisted.internet import defer 5 | 6 | from .. import data, helper 7 | from p2pool.util import pack 8 | 9 | 10 | #P2P_PREFIX = 'f9beb4d9'.decode('hex') # disk magic and old net magic 11 | P2P_PREFIX = 'e3e1f3e8'.decode('hex') # new net magic 12 | P2P_PORT = 8333 13 | ADDRESS_VERSION = 0 14 | ADDRESS_P2SH_VERSION = 5 15 | HUMAN_READABLE_PART = 'bitcoincash' 16 | RPC_PORT = 8332 17 | RPC_CHECK = defer.inlineCallbacks(lambda bitcoind: defer.returnValue( 18 | (yield helper.check_block_header(bitcoind, '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f')) and # genesis block 19 | (yield helper.check_block_header(bitcoind, '000000000000000000651ef99cb9fcbe0dadde1d424bd9f15ff20136191a5eec')) and # 478559 -- Bitcoin Cash fork 20 | (yield helper.check_block_header(bitcoind, '000000000000000001d956714215d96ffc00e0afda4cd0a96c96f8d802b1662b')) and # 556767 -- Bitcoin SV fork 21 | (yield bitcoind.rpc_getblockchaininfo())['chain'] == 'main' 22 | )) 23 | SUBSIDY_FUNC = lambda height: 50*100000000 >> (height + 1)//210000 24 | POW_FUNC = data.hash256 25 | BLOCK_PERIOD = 600 # s 26 | SYMBOL = 'BSV' 27 | CONF_FILE_FUNC = lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Bitcoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Bitcoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.bitcoin'), 'bitcoin.conf') 28 | BLOCK_EXPLORER_URL_PREFIX = 'https://blockchair.com/bitcoin-sv/block/' 29 | ADDRESS_EXPLORER_URL_PREFIX = 'https://blockchair.com/bitcoin-sv/address/' 30 | TX_EXPLORER_URL_PREFIX = 'https://blockchair.com/bitcoin-sv/transaction/' 31 | SANE_TARGET_RANGE = (2**256//2**32//100000000 - 1, 2**256//2**32 - 1) 32 | DUMB_SCRYPT_DIFF = 1 33 | DUST_THRESHOLD = 0.001e8 34 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/bitcoinsv_testnet.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | from twisted.internet import defer 5 | 6 | from .. import data, helper 7 | from p2pool.util import pack 8 | 9 | 10 | #P2P_PREFIX = '0b110907'.decode('hex') # disk magic and old netmagic 11 | P2P_PREFIX = 'f4e5f3f4'.decode('hex') # new net magic 12 | P2P_PORT = 18333 13 | ADDRESS_VERSION = 111 14 | ADDRESS_P2SH_VERSION = 196 15 | HUMAN_READABLE_PART = 'bchtest' 16 | RPC_PORT = 18332 17 | RPC_CHECK = defer.inlineCallbacks(lambda bitcoind: defer.returnValue( 18 | 'getreceivedbyaddress' in (yield bitcoind.rpc_help()) and 19 | (yield bitcoind.rpc_getinfo())['testnet'] 20 | )) 21 | SUBSIDY_FUNC = lambda height: 50*100000000 >> (height + 1)//210000 22 | POW_FUNC = data.hash256 23 | BLOCK_PERIOD = 600 # s 24 | SYMBOL = 'tBSV' 25 | CONF_FILE_FUNC = lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Bitcoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Bitcoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.bitcoin'), 'bitcoin.conf') 26 | BLOCK_EXPLORER_URL_PREFIX = 'https://test.whatsonchain.com/block-height/' 27 | ADDRESS_EXPLORER_URL_PREFIX = 'https://test.whatsonchain.com/tx/' 28 | TX_EXPLORER_URL_PREFIX = 'https://test.whatsonchain.com/address/' 29 | SANE_TARGET_RANGE = (2**256//2**32//100000000 - 1, 2**256//2**32 - 1) 30 | DUMB_SCRYPT_DIFF = 1 31 | DUST_THRESHOLD = 1e8 32 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/btcregtest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | from twisted.internet import defer 5 | 6 | from .. import data, helper 7 | from p2pool.util import pack 8 | 9 | 10 | P2P_PREFIX = 'fabfb5da'.decode('hex') 11 | P2P_PORT = 18444 12 | ADDRESS_VERSION = 111 13 | RPC_PORT = 28332 14 | RPC_CHECK = defer.inlineCallbacks(lambda bitcoind: defer.returnValue( 15 | 'bitcoin' in (yield bitcoind.rpc_help()) 16 | )) 17 | SUBSIDY_FUNC = lambda height: 50*100000000 >> (height + 1)//150 18 | POW_FUNC = data.hash256 19 | BLOCK_PERIOD = 600 # s 20 | SYMBOL = 'rBTC' 21 | CONF_FILE_FUNC = lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Bitcoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Bitcoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.bitcoin'), 'bitcoin.conf') 22 | BLOCK_EXPLORER_URL_PREFIX = '#' 23 | ADDRESS_EXPLORER_URL_PREFIX = '#' 24 | TX_EXPLORER_URL_PREFIX = '#' 25 | SANE_TARGET_RANGE = (2**256//2**32//1000 - 1, 2**256//2 - 1) 26 | DUMB_SCRYPT_DIFF = 1 27 | DUST_THRESHOLD = 1e8 28 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/fastcoin.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | from twisted.internet import defer 5 | 6 | from .. import data, helper 7 | from p2pool.util import pack 8 | 9 | 10 | P2P_PREFIX = 'fbc0b6db'.decode('hex') 11 | P2P_PORT = 9526 12 | ADDRESS_VERSION = 96 13 | RPC_PORT = 9527 14 | RPC_CHECK = defer.inlineCallbacks(lambda bitcoind: defer.returnValue( 15 | 'fastcoin' in (yield bitcoind.rpc_help()) and 16 | not (yield bitcoind.rpc_getinfo())['testnet'] 17 | )) 18 | SUBSIDY_FUNC = lambda height: 32*100000000 >> (height + 1)//2592000 19 | POW_FUNC = lambda data: pack.IntType(256).unpack(__import__('ltc_scrypt').getPoWHash(data)) 20 | BLOCK_PERIOD = 12 # s 21 | SYMBOL = 'FST' 22 | CONF_FILE_FUNC = lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Fastcoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Fastcoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.fastcoin'), 'fastcoin.conf') 23 | BLOCK_EXPLORER_URL_PREFIX = 'http://fst.blockexp.info/block/' 24 | ADDRESS_EXPLORER_URL_PREFIX = 'http://fst.blockexp.info/address/' 25 | TX_EXPLORER_URL_PREFIX = 'http://fst.blockexp.info/tx/' 26 | SANE_TARGET_RANGE = (2**256//100000000 - 1, 2**256//1000 - 1) 27 | DUMB_SCRYPT_DIFF = 2**16 28 | DUST_THRESHOLD = 0.03e8 29 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/litecoin.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | from twisted.internet import defer 5 | 6 | from .. import data, helper 7 | from p2pool.util import pack 8 | 9 | 10 | P2P_PREFIX = 'fbc0b6db'.decode('hex') 11 | P2P_PORT = 9333 12 | ADDRESS_VERSION = 48 13 | ADDRESS_P2SH_VERSION = 50 14 | HUMAN_READABLE_PART = 'ltc' 15 | RPC_PORT = 9332 16 | RPC_CHECK = defer.inlineCallbacks(lambda bitcoind: defer.returnValue( 17 | # 'litecoin' in (yield bitcoind.rpc_help()) and # new versions have "litecoinprivkey" but no "litecoinaddress" 18 | (yield helper.check_block_header(bitcoind, '12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2')) and 19 | (yield bitcoind.rpc_getblockchaininfo())['chain'] == 'main' 20 | )) 21 | SUBSIDY_FUNC = lambda height: 50*100000000 >> (height + 1)//840000 22 | POW_FUNC = lambda data: pack.IntType(256).unpack(__import__('ltc_scrypt').getPoWHash(data)) 23 | BLOCK_PERIOD = 150 # s 24 | SYMBOL = 'LTC' 25 | CONF_FILE_FUNC = lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Litecoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Litecoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.litecoin'), 'litecoin.conf') 26 | BLOCK_EXPLORER_URL_PREFIX = 'https://chainz.cryptoid.info/ltc/block.dws?' 27 | ADDRESS_EXPLORER_URL_PREFIX = 'https://chainz.cryptoid.info/ltc/address.dws?' 28 | TX_EXPLORER_URL_PREFIX = 'https://chainz.cryptoid.info/ltc/tx.dws?' 29 | SANE_TARGET_RANGE = (2**256//1000000000000000 - 1, 2**256//1000 - 1) 30 | DUMB_SCRYPT_DIFF = 2**16 31 | DUST_THRESHOLD = 0.03e8 32 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/litecoin_testnet.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | from twisted.internet import defer 5 | 6 | from .. import data, helper 7 | from p2pool.util import pack 8 | 9 | 10 | P2P_PREFIX = 'fdd2c8f1'.decode('hex') 11 | P2P_PORT = 19335 12 | ADDRESS_VERSION = 111 13 | ADDRESS_P2SH_VERSION = 58 14 | HUMAN_READABLE_PART = 'tltc' 15 | RPC_PORT = 19332 16 | RPC_CHECK = defer.inlineCallbacks(lambda bitcoind: defer.returnValue( 17 | 'getreceivedbyaddress' in (yield bitcoind.rpc_help()) and 18 | (yield bitcoind.rpc_getblockchaininfo())['chain'] == 'test' 19 | )) 20 | SUBSIDY_FUNC = lambda height: 50*100000000 >> (height + 1)//840000 21 | POW_FUNC = lambda data: pack.IntType(256).unpack(__import__('ltc_scrypt').getPoWHash(data)) 22 | BLOCK_PERIOD = 150 # s 23 | SYMBOL = 'tLTC' 24 | CONF_FILE_FUNC = lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Litecoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Litecoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.litecoin'), 'litecoin.conf') 25 | BLOCK_EXPLORER_URL_PREFIX = 'https://chain.so/block/LTCTEST/' 26 | ADDRESS_EXPLORER_URL_PREFIX = 'https://chain.so/address/LTCTEST/' 27 | TX_EXPLORER_URL_PREFIX = 'https://chain.so/tx/LTCTEST/' 28 | SANE_TARGET_RANGE = (1<<128-1, 1<<240-1) 29 | DUMB_SCRYPT_DIFF = 2**16 30 | DUST_THRESHOLD = 1e8 31 | SOFTFORKS_REQUIRED = set(['bip65', 'csv', 'segwit', 'mweb']) 32 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/namecoin.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | from twisted.internet import defer 5 | 6 | from .. import data, helper 7 | from p2pool.util import pack 8 | 9 | 10 | P2P_PREFIX = 'f9beb4fe'.decode('hex') 11 | P2P_PORT = 8334 12 | ADDRESS_VERSION = 52 13 | RPC_PORT = 8336 14 | RPC_CHECK = defer.inlineCallbacks(lambda bitcoind: defer.returnValue( 15 | 'namecoin' in (yield bitcoind.rpc_help()) and 16 | not (yield bitcoind.rpc_getinfo())['testnet'] 17 | )) 18 | SUBSIDY_FUNC = lambda height: 50*100000000 >> (height + 1)//210000 19 | POW_FUNC = data.hash256 20 | BLOCK_PERIOD = 600 # s 21 | SYMBOL = 'NMC' 22 | CONF_FILE_FUNC = lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Namecoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Namecoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.namecoin'), 'namecoin.conf') 23 | BLOCK_EXPLORER_URL_PREFIX = 'http://explorer.dot-bit.org/b/' 24 | ADDRESS_EXPLORER_URL_PREFIX = 'http://explorer.dot-bit.org/a/' 25 | TX_EXPLORER_URL_PREFIX = 'http://explorer.dot-bit.org/tx/' 26 | SANE_TARGET_RANGE = (2**256//2**32 - 1, 2**256//2**32 - 1) 27 | DUMB_SCRYPT_DIFF = 1 28 | DUST_THRESHOLD = 0.2e8 29 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/namecoin_testnet.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | from twisted.internet import defer 5 | 6 | from .. import data, helper 7 | from p2pool.util import pack 8 | 9 | 10 | P2P_PREFIX = 'fabfb5fe'.decode('hex') 11 | P2P_PORT = 18334 12 | ADDRESS_VERSION = 111 13 | RPC_PORT = 18336 14 | RPC_CHECK = defer.inlineCallbacks(lambda bitcoind: defer.returnValue( 15 | 'namecoin' in (yield bitcoind.rpc_help()) and 16 | (yield bitcoind.rpc_getinfo())['testnet'] 17 | )) 18 | SUBSIDY_FUNC = lambda height: 50*100000000 >> (height + 1)//210000 19 | POW_FUNC = data.hash256 20 | BLOCK_PERIOD = 600 # s 21 | SYMBOL = 'tNMC' 22 | CONF_FILE_FUNC = lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Namecoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Namecoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.namecoin'), 'namecoin.conf') 23 | BLOCK_EXPLORER_URL_PREFIX = 'http://testnet.explorer.dot-bit.org/b/' 24 | ADDRESS_EXPLORER_URL_PREFIX = 'http://testnet.explorer.dot-bit.org/a/' 25 | TX_EXPLORER_URL_PREFIX = 'http://testnet.explorer.dot-bit.org/tx/' 26 | SANE_TARGET_RANGE = (2**256//2**32 - 1, 2**256//2**32 - 1) 27 | DUMB_SCRYPT_DIFF = 1 28 | DUST_THRESHOLD = 1e8 29 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/terracoin.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | from twisted.internet import defer 5 | 6 | from .. import data, helper 7 | from p2pool.util import pack 8 | 9 | 10 | P2P_PREFIX = '42babe56'.decode('hex') 11 | P2P_PORT = 13333 12 | ADDRESS_VERSION = 0 13 | RPC_PORT = 13332 14 | RPC_CHECK = defer.inlineCallbacks(lambda bitcoind: defer.returnValue( 15 | 'terracoin' in (yield bitcoind.rpc_help()) and 16 | not (yield bitcoind.rpc_getinfo())['testnet'] 17 | )) 18 | SUBSIDY_FUNC = lambda height: 20*100000000 >> (height + 1)//1050000 19 | POW_FUNC = data.hash256 20 | BLOCK_PERIOD = 120 # s 21 | SYMBOL = 'TRC' 22 | CONF_FILE_FUNC = lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Terracoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Terracoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.terracoin'), 'terracoin.conf') 23 | BLOCK_EXPLORER_URL_PREFIX = 'http://bitinfocharts.com/terracoin/blockhash/' 24 | ADDRESS_EXPLORER_URL_PREFIX = 'http://bitinfocharts.com/terracoin/address/' 25 | TX_EXPLORER_URL_PREFIX = 'http://bitinfocharts.com/terracoin/tx/' 26 | SANE_TARGET_RANGE = (2**256//2**32//1000 - 1, 2**256//2**32 - 1) 27 | DUMB_SCRYPT_DIFF = 1 28 | DUST_THRESHOLD = 1e8 29 | -------------------------------------------------------------------------------- /p2pool/bitcoin/networks/terracoin_testnet.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | from twisted.internet import defer 5 | 6 | from .. import data, helper 7 | from p2pool.util import pack 8 | 9 | 10 | P2P_PREFIX = '41babe56'.decode('hex') 11 | P2P_PORT = 23333 12 | ADDRESS_VERSION = 111 13 | RPC_PORT = 23332 14 | RPC_CHECK = defer.inlineCallbacks(lambda bitcoind: defer.returnValue( 15 | 'terracoin' in (yield bitcoind.rpc_help()) and 16 | (yield bitcoind.rpc_getinfo())['testnet'] 17 | )) 18 | SUBSIDY_FUNC = lambda height: 20*100000000 >> (height + 1)//1050000 19 | POW_FUNC = data.hash256 20 | BLOCK_PERIOD = 120 # s 21 | SYMBOL = 'tTRC' 22 | CONF_FILE_FUNC = lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Terracoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Terracoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.terracoin'), 'terracoin.conf') 23 | BLOCK_EXPLORER_URL_PREFIX = 'http://trc.cryptocoinexplorer.com/testnet/block/' 24 | ADDRESS_EXPLORER_URL_PREFIX = 'http://trc.cryptocoinexplorer.com/testnet/address/' 25 | TX_EXPLORER_URL_PREFIX = 'http://trc.cryptocoinexplorer.com/testnet/tx/' 26 | SANE_TARGET_RANGE = (2**256//2**32//1000 - 1, 2**256//2**32 - 1) 27 | DUMB_SCRYPT_DIFF = 1 28 | DUST_THRESHOLD = 1e8 29 | -------------------------------------------------------------------------------- /p2pool/bitcoin/script.py: -------------------------------------------------------------------------------- 1 | from p2pool.util import math, pack 2 | import cStringIO as StringIO 3 | 4 | def reads_nothing(f): 5 | return None, f 6 | def protoPUSH(length): 7 | return lambda f: f.read(length) 8 | def protoPUSHDATA(size_len): 9 | def _(f): 10 | length_str = f.read(size_len) 11 | length = math.string_to_natural(length_str[::-1].lstrip(chr(0))) 12 | data = f.read(length) 13 | return data 14 | return _ 15 | 16 | opcodes = {} 17 | for i in xrange(256): 18 | opcodes[i] = 'UNK_' + str(i), reads_nothing 19 | 20 | opcodes[0] = 'PUSH', lambda f: ('', f) 21 | for i in xrange(1, 76): 22 | opcodes[i] = 'PUSH', protoPUSH(i) 23 | opcodes[76] = 'PUSH', protoPUSHDATA(1) 24 | opcodes[77] = 'PUSH', protoPUSHDATA(2) 25 | opcodes[78] = 'PUSH', protoPUSHDATA(4) 26 | opcodes[79] = 'PUSH', lambda f: ('\x81', f) 27 | for i in xrange(81, 97): 28 | opcodes[i] = 'PUSH', lambda f, _i=i: (chr(_i - 80), f) 29 | 30 | opcodes[172] = 'CHECKSIG', reads_nothing 31 | opcodes[173] = 'CHECKSIGVERIFY', reads_nothing 32 | opcodes[174] = 'CHECKMULTISIG', reads_nothing 33 | opcodes[175] = 'CHECKMULTISIGVERIFY', reads_nothing 34 | 35 | def parse(script): 36 | f = StringIO.StringIO(script) 37 | while pack.remaining(f): 38 | opcode_str = f.read(1) 39 | opcode = ord(opcode_str) 40 | opcode_name, read_func = opcodes[opcode] 41 | opcode_arg = read_func(f) 42 | yield opcode_name, opcode_arg 43 | 44 | def get_sigop_count(script): 45 | weights = { 46 | 'CHECKSIG': 1, 47 | 'CHECKSIGVERIFY': 1, 48 | 'CHECKMULTISIG': 20, 49 | 'CHECKMULTISIGVERIFY': 20, 50 | } 51 | return sum(weights.get(opcode_name, 0) for opcode_name, opcode_arg in parse(script)) 52 | 53 | def create_push_script(datums): # datums can be ints or strs 54 | res = [] 55 | for datum in datums: 56 | if isinstance(datum, (int, long)): 57 | if datum == -1 or 1 <= datum <= 16: 58 | res.append(chr(datum + 80)) 59 | continue 60 | negative = datum < 0 61 | datum = math.natural_to_string(abs(datum)) 62 | if datum and ord(datum[0]) & 128: 63 | datum = '\x00' + datum 64 | if negative: 65 | datum = chr(ord(datum[0]) + 128) + datum[1:] 66 | datum = datum[::-1] 67 | if len(datum) < 76: 68 | res.append(chr(len(datum))) 69 | elif len(datum) <= 0xff: 70 | res.append(76) 71 | res.append(chr(len(datum))) 72 | elif len(datum) <= 0xffff: 73 | res.append(77) 74 | res.append(pack.IntType(16).pack(len(datum))) 75 | elif len(datum) <= 0xffffffff: 76 | res.append(78) 77 | res.append(pack.IntType(32).pack(len(datum))) 78 | else: 79 | raise ValueError('string too long') 80 | res.append(datum) 81 | return ''.join(res) 82 | -------------------------------------------------------------------------------- /p2pool/bitcoin/sha256.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import struct 4 | 5 | 6 | k = [ 7 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 8 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 9 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 10 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 11 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 12 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 13 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 14 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, 15 | ] 16 | 17 | def process(state, chunk): 18 | def rightrotate(x, n): 19 | return (x >> n) | (x << 32 - n) % 2**32 20 | 21 | w = list(struct.unpack('>16I', chunk)) 22 | for i in xrange(16, 64): 23 | s0 = rightrotate(w[i-15], 7) ^ rightrotate(w[i-15], 18) ^ (w[i-15] >> 3) 24 | s1 = rightrotate(w[i-2], 17) ^ rightrotate(w[i-2], 19) ^ (w[i-2] >> 10) 25 | w.append((w[i-16] + s0 + w[i-7] + s1) % 2**32) 26 | 27 | a, b, c, d, e, f, g, h = start_state = struct.unpack('>8I', state) 28 | for k_i, w_i in zip(k, w): 29 | t1 = (h + (rightrotate(e, 6) ^ rightrotate(e, 11) ^ rightrotate(e, 25)) + ((e & f) ^ (~e & g)) + k_i + w_i) % 2**32 30 | 31 | a, b, c, d, e, f, g, h = ( 32 | (t1 + (rightrotate(a, 2) ^ rightrotate(a, 13) ^ rightrotate(a, 22)) + ((a & b) ^ (a & c) ^ (b & c))) % 2**32, 33 | a, b, c, (d + t1) % 2**32, e, f, g, 34 | ) 35 | 36 | return struct.pack('>8I', *((x + y) % 2**32 for x, y in zip(start_state, [a, b, c, d, e, f, g, h]))) 37 | 38 | 39 | initial_state = struct.pack('>8I', 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19) 40 | 41 | class sha256(object): 42 | digest_size = 256//8 43 | block_size = 512//8 44 | 45 | def __init__(self, data='', _=(initial_state, '', 0)): 46 | self.state, self.buf, self.length = _ 47 | self.update(data) 48 | 49 | def update(self, data): 50 | state = self.state 51 | buf = self.buf + data 52 | 53 | chunks = [buf[i:i + self.block_size] for i in xrange(0, len(buf) + 1, self.block_size)] 54 | for chunk in chunks[:-1]: 55 | state = process(state, chunk) 56 | 57 | self.state = state 58 | self.buf = chunks[-1] 59 | 60 | self.length += 8*len(data) 61 | 62 | def copy(self, data=''): 63 | return self.__class__(data, (self.state, self.buf, self.length)) 64 | 65 | def digest(self): 66 | state = self.state 67 | buf = self.buf + '\x80' + '\x00'*((self.block_size - 9 - len(self.buf)) % self.block_size) + struct.pack('>Q', self.length) 68 | 69 | for chunk in [buf[i:i + self.block_size] for i in xrange(0, len(buf), self.block_size)]: 70 | state = process(state, chunk) 71 | 72 | return state 73 | 74 | def hexdigest(self): 75 | return self.digest().encode('hex') 76 | -------------------------------------------------------------------------------- /p2pool/bitcoin/worker_interface.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import StringIO 4 | import json 5 | import random 6 | import sys 7 | 8 | from twisted.internet import defer 9 | 10 | import p2pool 11 | from p2pool.bitcoin import data as bitcoin_data, getwork 12 | from p2pool.util import expiring_dict, jsonrpc, pack, variable 13 | 14 | class _Provider(object): 15 | def __init__(self, parent, long_poll): 16 | self.parent = parent 17 | self.long_poll = long_poll 18 | 19 | def rpc_getwork(self, request, data=None): 20 | return self.parent._getwork(request, data, long_poll=self.long_poll) 21 | 22 | class _GETableServer(jsonrpc.HTTPServer): 23 | def __init__(self, provider, render_get_func): 24 | jsonrpc.HTTPServer.__init__(self, provider) 25 | self.render_GET = render_get_func 26 | 27 | class WorkerBridge(object): 28 | def __init__(self): 29 | self.new_work_event = variable.Event() 30 | 31 | def preprocess_request(self, request): 32 | return request, # *args to self.compute 33 | 34 | def get_work(self, request): 35 | raise NotImplementedError() 36 | 37 | class WorkerInterface(object): 38 | def __init__(self, worker_bridge): 39 | self.worker_bridge = worker_bridge 40 | 41 | self.worker_views = {} 42 | 43 | self.merkle_root_to_handler = expiring_dict.ExpiringDict(300) 44 | 45 | def attach_to(self, res, get_handler=None): 46 | res.putChild('', _GETableServer(_Provider(self, long_poll=False), get_handler)) 47 | 48 | def repost(request): 49 | request.content = StringIO.StringIO(json.dumps(dict(id=0, method='getwork'))) 50 | return s.render_POST(request) 51 | s = _GETableServer(_Provider(self, long_poll=True), repost) 52 | res.putChild('long-polling', s) 53 | 54 | @defer.inlineCallbacks 55 | def _getwork(self, request, data, long_poll): 56 | request.setHeader('X-Long-Polling', '/long-polling') 57 | request.setHeader('X-Roll-NTime', 'expire=100') 58 | request.setHeader('X-Is-P2Pool', 'true') 59 | if request.getHeader('Host') is not None: 60 | request.setHeader('X-Stratum', 'stratum+tcp://' + request.getHeader('Host')) 61 | 62 | if data is not None: 63 | header = getwork.decode_data(data) 64 | if header['merkle_root'] not in self.merkle_root_to_handler: 65 | print >>sys.stderr, '''Couldn't link returned work's merkle root with its handler. This should only happen if this process was recently restarted!''' 66 | defer.returnValue(False) 67 | defer.returnValue(self.merkle_root_to_handler[header['merkle_root']](header, request.getUser() if request.getUser() is not None else '', '\0'*self.worker_bridge.COINBASE_NONCE_LENGTH)) 68 | 69 | if p2pool.DEBUG: 70 | id = random.randrange(1000, 10000) 71 | print 'POLL %i START is_long_poll=%r user_agent=%r user=%r' % (id, long_poll, request.getHeader('User-Agent'), request.getUser()) 72 | 73 | if long_poll: 74 | request_id = request.getClientIP(), request.getHeader('Authorization') 75 | if self.worker_views.get(request_id, self.worker_bridge.new_work_event.times) != self.worker_bridge.new_work_event.times: 76 | if p2pool.DEBUG: 77 | print 'POLL %i PUSH' % (id,) 78 | else: 79 | if p2pool.DEBUG: 80 | print 'POLL %i WAITING' % (id,) 81 | yield self.worker_bridge.new_work_event.get_deferred() 82 | self.worker_views[request_id] = self.worker_bridge.new_work_event.times 83 | 84 | x, handler = self.worker_bridge.get_work(*self.worker_bridge.preprocess_request(request.getUser() if request.getUser() is not None else '')) 85 | res = getwork.BlockAttempt( 86 | version=x['version'], 87 | previous_block=x['previous_block'], 88 | merkle_root=bitcoin_data.check_merkle_link(bitcoin_data.hash256(x['coinb1'] + '\0'*self.worker_bridge.COINBASE_NONCE_LENGTH + x['coinb2']), x['merkle_link']), 89 | timestamp=x['timestamp'], 90 | bits=x['bits'], 91 | share_target=x['share_target'], 92 | ) 93 | assert res.merkle_root not in self.merkle_root_to_handler 94 | 95 | self.merkle_root_to_handler[res.merkle_root] = handler 96 | 97 | if p2pool.DEBUG: 98 | print 'POLL %i END identifier=%i' % (id, self.worker_bridge.new_work_event.times) 99 | 100 | extra_params = {} 101 | if request.getHeader('User-Agent') == 'Jephis PIC Miner': 102 | # ASICMINER BE Blades apparently have a buffer overflow bug and 103 | # can't handle much extra in the getwork response 104 | extra_params = {} 105 | else: 106 | extra_params = dict(identifier=str(self.worker_bridge.new_work_event.times), submitold=True) 107 | defer.returnValue(res.getwork(**extra_params)) 108 | 109 | class CachingWorkerBridge(object): 110 | def __init__(self, inner): 111 | self._inner = inner 112 | self.net = self._inner.net 113 | 114 | self.COINBASE_NONCE_LENGTH = (inner.COINBASE_NONCE_LENGTH+1)//2 115 | self.new_work_event = inner.new_work_event 116 | self.preprocess_request = inner.preprocess_request 117 | 118 | self._my_bits = (self._inner.COINBASE_NONCE_LENGTH - self.COINBASE_NONCE_LENGTH)*8 119 | 120 | self._cache = {} 121 | self._times = None 122 | 123 | def get_work(self, user, address, desired_share_target, 124 | desired_pseudoshare_target, worker_ip=None, *args): 125 | if self._times != self.new_work_event.times: 126 | self._cache = {} 127 | self._times = self.new_work_event.times 128 | 129 | cachekey = (address, desired_share_target, args) 130 | if cachekey not in self._cache: 131 | x, handler = self._inner.get_work(user, address, desired_share_target, 132 | desired_pseudoshare_target, worker_ip, *args) 133 | self._cache[cachekey] = x, handler, 0 134 | 135 | x, handler, nonce = self._cache.pop(cachekey) 136 | 137 | res = ( 138 | dict(x, coinb1=x['coinb1'] + pack.IntType(self._my_bits).pack(nonce)), 139 | lambda header, user, coinbase_nonce, pseudoshare_target: handler(header, user, pack.IntType(self._my_bits).pack(nonce) + coinbase_nonce, pseudoshare_target), 140 | ) 141 | 142 | if nonce + 1 != 2**self._my_bits: 143 | self._cache[cachekey] = x, handler, nonce + 1 144 | 145 | return res 146 | def __getattr__(self, attr): 147 | return getattr(self._inner, attr) 148 | -------------------------------------------------------------------------------- /p2pool/networks/__init__.py: -------------------------------------------------------------------------------- 1 | import pkgutil 2 | 3 | nets = dict((name, __import__(name, globals(), fromlist="dummy")) 4 | for module_loader, name, ispkg in pkgutil.iter_modules(__path__)) 5 | for net_name, net in nets.iteritems(): 6 | net.NAME = net_name 7 | -------------------------------------------------------------------------------- /p2pool/networks/bitcoin.py: -------------------------------------------------------------------------------- 1 | from p2pool.bitcoin import networks 2 | 3 | # CHAIN_LENGTH = number of shares back client keeps 4 | # REAL_CHAIN_LENGTH = maximum number of shares back client uses to compute payout 5 | # REAL_CHAIN_LENGTH must always be <= CHAIN_LENGTH 6 | # REAL_CHAIN_LENGTH must be changed in sync with all other clients 7 | # changes can be done by changing one, then the other 8 | 9 | PARENT = networks.nets['bitcoin'] 10 | SHARE_PERIOD = 30 # seconds 11 | CHAIN_LENGTH = 24*60*60//10 # shares 12 | REAL_CHAIN_LENGTH = 24*60*60//10 # shares 13 | TARGET_LOOKBEHIND = 200 # shares 14 | SPREAD = 3 # blocks 15 | IDENTIFIER = 'fc70035c7a81bc6f'.decode('hex') 16 | PREFIX = '2472ef181efcd37b'.decode('hex') 17 | P2P_PORT = 9333 18 | MIN_TARGET = 0 19 | MAX_TARGET = 2**256//2**32 - 1 20 | PERSIST = True 21 | WORKER_PORT = 9332 22 | BOOTSTRAP_ADDRS = [ 23 | 'ml.toom.im', 24 | 'ml.toom.im:9336', 25 | 'ml.toom.im:9334', 26 | 'btc-fork.coinpool.pw:9335', 27 | 'crypto.office-on-the.net:9335', 28 | 'btc.p2pool.leblancnet.us', 29 | ] 30 | ANNOUNCE_CHANNEL = '#p2pool' 31 | VERSION_CHECK = lambda v: None if 100000 <= v else 'Bitcoin version too old. Upgrade to 0.11.2 or newer!' # not a bug. BIP65 support is ensured by SOFTFORKS_REQUIRED 32 | VERSION_WARNING = lambda v: None 33 | SOFTFORKS_REQUIRED = set(['bip65', 'csv', 'segwit']) 34 | MINIMUM_PROTOCOL_VERSION = 3301 35 | SEGWIT_ACTIVATION_VERSION = 33 36 | BLOCK_MAX_SIZE = 1000000 37 | BLOCK_MAX_WEIGHT = 4000000 38 | -------------------------------------------------------------------------------- /p2pool/networks/bitcoin_testnet.py: -------------------------------------------------------------------------------- 1 | from p2pool.bitcoin import networks 2 | 3 | PARENT = networks.nets['bitcoin_testnet'] 4 | SHARE_PERIOD = 30 # seconds 5 | CHAIN_LENGTH = 60*60//10 # shares 6 | REAL_CHAIN_LENGTH = 60*60//10 # shares 7 | TARGET_LOOKBEHIND = 200 # shares 8 | SPREAD = 3 # blocks 9 | IDENTIFIER = '5fc2be2d4f0d6bfb'.decode('hex') 10 | PREFIX = '3f6057a15036f441'.decode('hex') 11 | P2P_PORT = 19333 12 | MIN_TARGET = 0 13 | MAX_TARGET = 2**256//2**32 - 1 14 | PERSIST = False 15 | WORKER_PORT = 19332 16 | BOOTSTRAP_ADDRS = 'forre.st liteco.in 78.158.149.247'.split(' ') 17 | ANNOUNCE_CHANNEL = '#p2pool-alt' 18 | VERSION_CHECK = lambda v: None if 100000 <= v else 'Bitcoin version too old. Upgrade to 0.11.2 or newer!' # not a bug. BIP65 support is ensured by SOFTFORKS_REQUIRED 19 | VERSION_WARNING = lambda v: None 20 | SOFTFORKS_REQUIRED = set(['bip65', 'csv', 'segwit']) 21 | MINIMUM_PROTOCOL_VERSION = 3200 22 | SEGWIT_ACTIVATION_VERSION = 15 23 | BLOCK_MAX_SIZE = 1000000 24 | BLOCK_MAX_WEIGHT = 4000000 25 | -------------------------------------------------------------------------------- /p2pool/networks/bitcoincash.py: -------------------------------------------------------------------------------- 1 | from p2pool.bitcoin import networks 2 | 3 | # CHAIN_LENGTH = number of shares back client keeps 4 | # REAL_CHAIN_LENGTH = maximum number of shares back client uses to compute payout 5 | # REAL_CHAIN_LENGTH must always be <= CHAIN_LENGTH 6 | # REAL_CHAIN_LENGTH must be changed in sync with all other clients 7 | # changes can be done by changing one, then the other 8 | 9 | PARENT = networks.nets['bitcoincash'] 10 | SHARE_PERIOD = 60 # seconds -- one minute 11 | CHAIN_LENGTH = 3*24*60 # shares -- three days 12 | REAL_CHAIN_LENGTH = 3*24*60 # shares -- three days 13 | TARGET_LOOKBEHIND = 200 # shares 14 | SPREAD = 3 # blocks 15 | IDENTIFIER = 'b826c0a51ddc2d2b'.decode('hex') 16 | PREFIX = 'ac9a8fda9a911bce'.decode('hex') 17 | P2P_PORT = 9349 18 | MIN_TARGET = 0 19 | MAX_TARGET = 2**256//2**32 - 1 20 | PERSIST = True # Set to False for solo mining or starting a new chain 21 | WORKER_PORT = 9348 22 | BOOTSTRAP_ADDRS = [ 23 | 'ml.toom.im', 24 | 'bch.p2pool.leblancnet.us', 25 | 'siberia.mine.nu', 26 | '5.8.79.155', 27 | '18.209.181.17', 28 | '95.79.35.133', 29 | '193.29.58.47', 30 | ] 31 | ANNOUNCE_CHANNEL = '#p2pool' 32 | VERSION_CHECK = lambda v: None if 100000 <= v else 'Bitcoin version too old. Upgrade to 0.11.2 or newer!' # not a bug. BIP65 support is ensured by SOFTFORKS_REQUIRED 33 | VERSION_WARNING = lambda v: None 34 | SOFTFORKS_REQUIRED = set() 35 | MINIMUM_PROTOCOL_VERSION = 3301 36 | BLOCK_MAX_SIZE = 32000000 37 | BLOCK_MAX_WEIGHT = 128000000 38 | -------------------------------------------------------------------------------- /p2pool/networks/bitcoincash_testnet.py: -------------------------------------------------------------------------------- 1 | from p2pool.bitcoin import networks 2 | 3 | PARENT = networks.nets['bitcoincash_testnet'] 4 | SHARE_PERIOD = 60 # seconds -- one minute 5 | CHAIN_LENGTH = 3*24*60 # shares -- three days 6 | REAL_CHAIN_LENGTH = 3*24*60 # shares -- three days 7 | TARGET_LOOKBEHIND = 200 # shares 8 | SPREAD = 3 # blocks 9 | IDENTIFIER = 'c9f3de8d9508faef'.decode('hex') 10 | PREFIX = '08c5541df85a8a65'.decode('hex') 11 | P2P_PORT = 19339 12 | MIN_TARGET = 0 13 | MAX_TARGET = 2**256//2**32 - 1 14 | PERSIST = False 15 | WORKER_PORT = 19338 16 | BOOTSTRAP_ADDRS = 'forre.st liteco.in 78.158.149.247'.split(' ') 17 | ANNOUNCE_CHANNEL = '#p2pool-alt' 18 | VERSION_CHECK = lambda v: None if 100000 <= v else 'Bitcoin version too old. Upgrade to 0.11.2 or newer!' # not a bug. BIP65 support is ensured by SOFTFORKS_REQUIRED 19 | VERSION_WARNING = lambda v: None 20 | SOFTFORKS_REQUIRED = set() 21 | MINIMUM_PROTOCOL_VERSION = 3301 22 | BLOCK_MAX_SIZE = 32000000 23 | BLOCK_MAX_WEIGHT = 128000000 24 | -------------------------------------------------------------------------------- /p2pool/networks/bitcoinsv.py: -------------------------------------------------------------------------------- 1 | from p2pool.bitcoin import networks 2 | 3 | # CHAIN_LENGTH = number of shares back client keeps 4 | # REAL_CHAIN_LENGTH = maximum number of shares back client uses to compute payout 5 | # REAL_CHAIN_LENGTH must always be <= CHAIN_LENGTH 6 | # REAL_CHAIN_LENGTH must be changed in sync with all other clients 7 | # changes can be done by changing one, then the other 8 | 9 | PARENT = networks.nets['bitcoinsv'] 10 | SHARE_PERIOD = 30 # seconds 11 | CHAIN_LENGTH = 24*60*60//30 # shares 12 | REAL_CHAIN_LENGTH = 24*60*60//30 # shares 13 | TARGET_LOOKBEHIND = 200 # shares 14 | SPREAD = 3 # blocks 15 | IDENTIFIER = '1c051dec1abcd71d'.decode('hex') 16 | PREFIX = '1c051dee6ec8bb1e'.decode('hex') 17 | P2P_PORT = 6338 18 | MIN_TARGET = 0 19 | MAX_TARGET = 2**256//2**32 - 1 20 | PERSIST = True 21 | WORKER_PORT = 6339 22 | BOOTSTRAP_ADDRS = [ 23 | 'p2p-usa.xyz', 24 | 'p2p-south.xyz', 25 | 'p2p-ekb.xyz', 26 | 'msk.p2pool.site', 27 | 'nn.p2pool.site', 28 | ] 29 | ANNOUNCE_CHANNEL = '#p2pool' 30 | VERSION_CHECK = lambda v: None if 100000 <= v else 'Bitcoin version too old. Upgrade to 0.11.2 or newer!' # not a bug. BIP65 support is ensured by SOFTFORKS_REQUIRED 31 | VERSION_WARNING = lambda v: None 32 | SOFTFORKS_REQUIRED = set(['bip65', 'csv']) 33 | MINIMUM_PROTOCOL_VERSION = 3301 34 | BLOCK_MAX_SIZE = 32000000 35 | BLOCK_MAX_WEIGHT = 128000000 36 | -------------------------------------------------------------------------------- /p2pool/networks/bitcoinsv_testnet.py: -------------------------------------------------------------------------------- 1 | from p2pool.bitcoin import networks 2 | 3 | PARENT = networks.nets['bitcoinsv_testnet'] 4 | SHARE_PERIOD = 30 # seconds 5 | CHAIN_LENGTH = 24*60*60//30 # shares 6 | REAL_CHAIN_LENGTH = 24*60*60//30 # shares 7 | TARGET_LOOKBEHIND = 200 # shares 8 | SPREAD = 3 # blocks 9 | IDENTIFIER = '1c051dee6ec8bb1e'.decode('hex') 10 | PREFIX = '4B62545B1A631A5C'.decode('hex') 11 | P2P_PORT = 16338 12 | MIN_TARGET = 0 13 | MAX_TARGET = 2**256//2**32 - 1 14 | PERSIST = False 15 | WORKER_PORT = 16339 16 | BOOTSTRAP_ADDRS = ''.split(' ') 17 | ANNOUNCE_CHANNEL = '#p2pool-alt' 18 | VERSION_CHECK = lambda v: None if 100000 <= v else 'Bitcoin version too old. Upgrade to 0.11.2 or newer!' # not a bug. BIP65 support is ensured by SOFTFORKS_REQUIRED 19 | VERSION_WARNING = lambda v: None 20 | SOFTFORKS_REQUIRED = set(['bip65', 'csv']) 21 | MINIMUM_PROTOCOL_VERSION = 3301 22 | BLOCK_MAX_SIZE = 32000000 23 | BLOCK_MAX_WEIGHT = 128000000 24 | -------------------------------------------------------------------------------- /p2pool/networks/btcregtest.py: -------------------------------------------------------------------------------- 1 | from p2pool.bitcoin import networks 2 | 3 | PARENT = networks.nets['btcregtest'] 4 | SHARE_PERIOD = 30 # seconds 5 | CHAIN_LENGTH = 60*60//10 # shares 6 | REAL_CHAIN_LENGTH = 60*60//10 # shares 7 | TARGET_LOOKBEHIND = 200 # shares 8 | SPREAD = 3 # blocks 9 | IDENTIFIER = '5ad2c6ecbd7d9372'.decode('hex') 10 | PREFIX = '8f2c8d54b3278bc8'.decode('hex') 11 | P2P_PORT = 19444 12 | MIN_TARGET = 0 13 | MAX_TARGET = 2**256//2 - 1 14 | PERSIST = False 15 | WORKER_PORT = 19443 16 | BOOTSTRAP_ADDRS = [] 17 | ANNOUNCE_CHANNEL = '#p2pool-alt' 18 | VERSION_CHECK = lambda v: None if 100000 <= v else 'Bitcoin version too old. Upgrade to 0.11.2 or newer!' # not a bug. BIP65 support is ensured by SOFTFORKS_REQUIRED 19 | VERSION_WARNING = lambda v: None 20 | SOFTFORKS_REQUIRED = set(['bip65', 'csv', 'segwit', 'segwit2x']) 21 | MINIMUM_PROTOCOL_VERSION = 3200 22 | SEGWIT_ACTIVATION_VERSION = 15 23 | BLOCK_MAX_SIZE = 1000000 24 | BLOCK_MAX_WEIGHT = 4000000 25 | -------------------------------------------------------------------------------- /p2pool/networks/fastcoin.py: -------------------------------------------------------------------------------- 1 | from p2pool.bitcoin import networks 2 | 3 | PARENT = networks.nets['fastcoin'] 4 | SHARE_PERIOD = 6 # seconds 5 | NEW_SHARE_PERIOD = 6 # seconds 6 | CHAIN_LENGTH = 24*60*60//10 # shares 7 | REAL_CHAIN_LENGTH = 24*60*60//10 # shares 8 | TARGET_LOOKBEHIND = 60 # shares 9 | SPREAD = 150 # blocks 10 | NEW_SPREAD = 150 # blocks 11 | IDENTIFIER = '9f2e390aa41ffade'.decode('hex') 12 | PREFIX = '50f713ab040dfade'.decode('hex') 13 | P2P_PORT = 23660 14 | MIN_TARGET = 0 15 | MAX_TARGET = 2**256//2**20 - 1 16 | PERSIST = True 17 | WORKER_PORT = 5150 18 | BOOTSTRAP_ADDRS = 'inetrader.com'.split(' ') 19 | ANNOUNCE_CHANNEL = '#p2pool-fst' 20 | VERSION_CHECK = lambda v: True 21 | VERSION_WARNING = lambda v: 'Upgrade Fastcoin to >= 0.10.2.2!' if v < 100202 else None 22 | BLOCK_MAX_SIZE = 1000000 23 | BLOCK_MAX_WEIGHT = 4000000 24 | -------------------------------------------------------------------------------- /p2pool/networks/litecoin.py: -------------------------------------------------------------------------------- 1 | from p2pool.bitcoin import networks 2 | 3 | PARENT = networks.nets['litecoin'] 4 | SHARE_PERIOD = 15 # seconds 5 | CHAIN_LENGTH = 24*60*60//10 # shares 6 | REAL_CHAIN_LENGTH = 24*60*60//10 # shares 7 | TARGET_LOOKBEHIND = 200 # shares 8 | SPREAD = 3 # blocks 9 | IDENTIFIER = 'e037d5b8c6923410'.decode('hex') 10 | PREFIX = '7208c1a53ef629b0'.decode('hex') 11 | P2P_PORT = 9326 12 | MIN_TARGET = 0 13 | MAX_TARGET = 2**256//2**20 - 1 14 | PERSIST = True 15 | WORKER_PORT = 9327 16 | BOOTSTRAP_ADDRS = [ 17 | 'crypto.office-on-the.net', 18 | 'ltc.p2pool.leblancnet.us', 19 | '51.148.43.34', 20 | '68.131.29.131', 21 | '87.102.46.100', 22 | '89.237.60.231', 23 | '95.79.35.133', 24 | '96.255.61.32', 25 | '174.56.93.93', 26 | '178.238.236.130', 27 | '194.190.93.235', 28 | ] 29 | ANNOUNCE_CHANNEL = '#p2pool-ltc' 30 | VERSION_CHECK = lambda v: None if 100400 <= v else 'Litecoin version too old. Upgrade to 0.10.4 or newer!' 31 | VERSION_WARNING = lambda v: None 32 | SOFTFORKS_REQUIRED = set(['bip65', 'csv', 'segwit', 'taproot', 'mweb']) 33 | MINIMUM_PROTOCOL_VERSION = 3301 34 | SEGWIT_ACTIVATION_VERSION = 17 35 | BLOCK_MAX_SIZE = 1000000 36 | BLOCK_MAX_WEIGHT = 4000000 37 | -------------------------------------------------------------------------------- /p2pool/networks/litecoin_testnet.py: -------------------------------------------------------------------------------- 1 | from p2pool.bitcoin import networks 2 | 3 | PARENT = networks.nets['litecoin_testnet'] 4 | SHARE_PERIOD = 4 # seconds 5 | CHAIN_LENGTH = 20*60//3 # shares 6 | REAL_CHAIN_LENGTH = 20*60//3 # shares 7 | TARGET_LOOKBEHIND = 200 # shares 8 | SPREAD = 3 # blocks 9 | IDENTIFIER = 'cca5e24ec6408b1e'.decode('hex') 10 | PREFIX = 'ad9614f6466a39cf'.decode('hex') 11 | P2P_PORT = 19338 12 | MIN_TARGET = 0 13 | MAX_TARGET = 2**256//20 - 1 14 | PERSIST = False 15 | WORKER_PORT = 19327 16 | BOOTSTRAP_ADDRS = 'forre.st'.split(' ') 17 | ANNOUNCE_CHANNEL = '#p2pool-alt' 18 | VERSION_CHECK = lambda v: True 19 | SOFTFORKS_REQUIRED = set(['bip65', 'csv', 'segwit', 'taproot', 'mweb']) 20 | MINIMUM_PROTOCOL_VERSION = 3301 21 | SEGWIT_ACTIVATION_VERSION = 17 22 | BLOCK_MAX_SIZE = 1000000 23 | BLOCK_MAX_WEIGHT = 4000000 24 | -------------------------------------------------------------------------------- /p2pool/networks/terracoin.py: -------------------------------------------------------------------------------- 1 | from p2pool.bitcoin import networks 2 | 3 | PARENT = networks.nets['terracoin'] 4 | SHARE_PERIOD = 45 # seconds 5 | CHAIN_LENGTH = 24*60*60//30 # shares 6 | REAL_CHAIN_LENGTH = 24*60*60//30 # shares 7 | TARGET_LOOKBEHIND = 200 # shares 8 | SPREAD = 10 # blocks 9 | IDENTIFIER = 'a42a265ad1b6d42b'.decode('hex') 10 | PREFIX = '56a3f62173d2a9b5'.decode('hex') 11 | P2P_PORT = 9323 12 | MIN_TARGET = 0 13 | MAX_TARGET = 2**256//2**32 - 1 14 | PERSIST = True 15 | WORKER_PORT = 9322 16 | BOOTSTRAP_ADDRS = 'seed1.p2pool.terracoin.org seed2.p2pool.terracoin.org seed3.p2pool.terracoin.org forre.st 93.97.192.93 66.90.73.83 67.83.108.0 219.84.64.174 24.167.17.248 109.74.195.142 83.211.86.49 94.23.34.145 168.7.116.243 94.174.40.189:9344 89.79.79.195 portals94.ns01.us p2pool.org'.split(' ') 17 | ANNOUNCE_CHANNEL = '#p2pool-alt' 18 | VERSION_CHECK = lambda v: 80002 <= v 19 | VERSION_WARNING = lambda v: 'Upgrade Terracoin to >= 0.8.0.4!' if v < 80004 else None 20 | BLOCK_MAX_SIZE = 1000000 21 | BLOCK_MAX_WEIGHT = 4000000 22 | -------------------------------------------------------------------------------- /p2pool/networks/terracoin_testnet.py: -------------------------------------------------------------------------------- 1 | from p2pool.bitcoin import networks 2 | 3 | PARENT = networks.nets['terracoin_testnet'] 4 | SHARE_PERIOD = 45 # seconds 5 | CHAIN_LENGTH = 60*60//30 # shares 6 | REAL_CHAIN_LENGTH = 60*60//30 # shares 7 | TARGET_LOOKBEHIND = 200 # shares 8 | SPREAD = 10 # blocks 9 | IDENTIFIER = 'b41a282ca5b2d85a'.decode('hex') 10 | PREFIX = '16d2b91182dab8a4'.decode('hex') 11 | P2P_PORT = 19323 12 | MIN_TARGET = 0 13 | MAX_TARGET = 2**256//2**32 - 1 14 | PERSIST = False 15 | WORKER_PORT = 19322 16 | BOOTSTRAP_ADDRS = 'seed1.p2pool.terracoin.org seed2.p2pool.terracoin.org seed3.p2pool.terracoin.org forre.st'.split(' ') 17 | ANNOUNCE_CHANNEL = '#p2pool-alt' 18 | VERSION_CHECK = lambda v: True 19 | VERSION_WARNING = lambda v: 'Upgrade Terracoin to >= 0.8.0.4!' if v < 80004 else None 20 | BLOCK_MAX_SIZE = 1000000 21 | BLOCK_MAX_WEIGHT = 4000000 22 | -------------------------------------------------------------------------------- /p2pool/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtoomim/p2pool/ece15b03fc2367d02e730b2b85b666217fa8985e/p2pool/test/__init__.py -------------------------------------------------------------------------------- /p2pool/test/bitcoin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtoomim/p2pool/ece15b03fc2367d02e730b2b85b666217fa8985e/p2pool/test/bitcoin/__init__.py -------------------------------------------------------------------------------- /p2pool/test/bitcoin/test_getwork.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from p2pool.bitcoin import getwork, data as bitcoin_data 4 | 5 | class Test(unittest.TestCase): 6 | def test_all(self): 7 | cases = [ 8 | { 9 | 'target': '0000000000000000000000000000000000000000000000f2b944000000000000', 10 | 'midstate': '5982f893102dec03e374b472647c4f19b1b6d21ae4b2ac624f3d2f41b9719404', 11 | 'hash1': '00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000', 12 | 'data': '0000000163930d52a5ffca79b29b95a659a302cd4e1654194780499000002274000000002e133d9e51f45bc0886d05252038e421e82bff18b67dc14b90d9c3c2f422cd5c4dd4598e1a44b9f200000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000' 13 | }, 14 | { 15 | 'midstate' : 'f4a9b048c0cb9791bc94b13ee0eec21e713963d524fd140b58bb754dd7b0955f', 16 | 'data' : '000000019a1d7342fb62090bda686b22d90f9f73d0f5c418b9c980cd0000011a00000000680b07c8a2f97ecd831f951806857e09f98a3b81cdef1fa71982934fef8dc3444e18585d1a0abbcf00000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000', 17 | 'hash1' : '00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000', 18 | 'target' : '0000000000000000000000000000000000000000000000cfbb0a000000000000', 19 | 'extrathing': 'hi!', 20 | }, 21 | { 22 | 'data' : '000000019a1d7342fb62090bda686b22d90f9f73d0f5c418b9c980cd0000011a00000000680b07c8a2f97ecd831f951806857e09f98a3b81cdef1fa71982934fef8dc3444e18585d1a0abbcf00000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000', 23 | 'hash1' : '00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000', 24 | 'target' : '0000000000000000000000000000000000000000000000cfbb0a000000000000', 25 | 'extrathing': 'hi!', 26 | }, 27 | ] 28 | for case in cases: 29 | ba = getwork.BlockAttempt.from_getwork(case) 30 | 31 | extra = dict(case) 32 | del extra['data'], extra['hash1'], extra['target'] 33 | extra.pop('midstate', None) 34 | 35 | getwork_check = ba.getwork(**extra) 36 | assert getwork_check == case or dict((k, v) for k, v in getwork_check.iteritems() if k != 'midstate') == case 37 | 38 | case2s = [ 39 | getwork.BlockAttempt( 40 | 1, 41 | 0x148135e10208db85abb62754341a392eab1f186aab077a831cf7, 42 | 0x534ea08be1ab529f484369344b6d5423ef5a0767db9b3ebb4e182bbb67962520, 43 | 1305759879, 44 | bitcoin_data.FloatingInteger.from_target_upper_bound(0x44b9f20000000000000000000000000000000000000000000000), 45 | 0x44b9f20000000000000000000000000000000000000000000000, 46 | ), 47 | getwork.BlockAttempt( 48 | 1, 49 | 0x148135e10208db85abb62754341a392eab1f186aab077a831cf7, 50 | 0x534ea08be1ab529f484369344b6d5423ef5a0767db9b3ebb4e182bbb67962520, 51 | 1305759879, 52 | bitcoin_data.FloatingInteger.from_target_upper_bound(0x44b9f20000000000000000000000000000000000000000000000), 53 | 432*2**230, 54 | ), 55 | getwork.BlockAttempt( 56 | 1, 57 | 0x148135e10208db85abb62754341a392eab1f186aab077a831cf7, 58 | 0x534ea08be1ab529f484369344b6d5423ef5a0767db9b3ebb4e182bbb67962520, 59 | 1305759879, 60 | bitcoin_data.FloatingInteger.from_target_upper_bound(0x44b9f20000000000000000000000000000000000000000000000), 61 | 7*2**240, 62 | ) 63 | ] 64 | for case2 in case2s: 65 | assert getwork.BlockAttempt.from_getwork(case2.getwork()) == case2 66 | assert getwork.BlockAttempt.from_getwork(case2.getwork(ident='hi')) == case2 67 | case2 = case2.update(previous_block=case2.previous_block - 10) 68 | assert getwork.BlockAttempt.from_getwork(case2.getwork()) == case2 69 | assert getwork.BlockAttempt.from_getwork(case2.getwork(ident='hi')) == case2 70 | -------------------------------------------------------------------------------- /p2pool/test/bitcoin/test_p2p.py: -------------------------------------------------------------------------------- 1 | from twisted.internet import defer, reactor 2 | from twisted.trial import unittest 3 | 4 | from p2pool.bitcoin import data, networks, p2p 5 | from p2pool.util import deferral 6 | 7 | 8 | class Test(unittest.TestCase): 9 | @defer.inlineCallbacks 10 | def test_get_block(self): 11 | factory = p2p.ClientFactory(networks.nets['bitcoin']) 12 | c = reactor.connectTCP('127.0.0.1', 8333, factory) 13 | try: 14 | h = 0x000000000000046acff93b0e76cd10490551bf871ce9ac9fad62e67a07ff1d1e 15 | block = yield deferral.retry()(defer.inlineCallbacks(lambda: defer.returnValue((yield (yield factory.getProtocol()).get_block(h)))))() 16 | assert data.merkle_hash(map(data.get_txid, block['txs'])) == block['header']['merkle_root'] 17 | assert data.hash256(data.block_header_type.pack(block['header'])) == h 18 | finally: 19 | factory.stopTrying() 20 | c.disconnect() 21 | -------------------------------------------------------------------------------- /p2pool/test/bitcoin/test_script.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from p2pool.bitcoin import script 4 | 5 | class Test(unittest.TestCase): 6 | def test_all(self): 7 | data = '76 A9 14 89 AB CD EF AB BA AB BA AB BA AB BA AB BA AB BA AB BA AB BA 88 AC'.replace(' ', '').decode('hex') 8 | self.assertEquals( 9 | list(script.parse(data)), 10 | [('UNK_118', None), ('UNK_169', None), ('PUSH', '\x89\xab\xcd\xef\xab\xba\xab\xba\xab\xba\xab\xba\xab\xba\xab\xba\xab\xba\xab\xba'), ('UNK_136', None), ('CHECKSIG', None)], 11 | ) 12 | self.assertEquals(script.get_sigop_count(data), 1) 13 | -------------------------------------------------------------------------------- /p2pool/test/bitcoin/test_sha256.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import unittest 4 | import hashlib 5 | import random 6 | 7 | from p2pool.bitcoin import sha256 8 | 9 | class Test(unittest.TestCase): 10 | def test_all(self): 11 | for test in ['', 'a', 'b', 'abc', 'abc'*50, 'hello world']: 12 | #print test 13 | #print sha256.sha256(test).hexdigest() 14 | #print hashlib.sha256(test).hexdigest() 15 | #print 16 | assert sha256.sha256(test).hexdigest() == hashlib.sha256(test).hexdigest() 17 | def random_str(l): 18 | return ''.join(chr(random.randrange(256)) for i in xrange(l)) 19 | for length in xrange(150): 20 | test = random_str(length) 21 | a = sha256.sha256(test).hexdigest() 22 | b = hashlib.sha256(test).hexdigest() 23 | assert a == b 24 | for i in xrange(100): 25 | test = random_str(int(random.expovariate(1/100))) 26 | test2 = random_str(int(random.expovariate(1/100))) 27 | 28 | a = sha256.sha256(test) 29 | a = a.copy() 30 | a.update(test2) 31 | a = a.hexdigest() 32 | 33 | b = hashlib.sha256(test) 34 | b = b.copy() 35 | b.update(test2) 36 | b = b.hexdigest() 37 | assert a == b 38 | -------------------------------------------------------------------------------- /p2pool/test/test_data.py: -------------------------------------------------------------------------------- 1 | import random 2 | import unittest 3 | 4 | from p2pool import data 5 | from p2pool.bitcoin import data as bitcoin_data 6 | from p2pool.test.util import test_forest 7 | from p2pool.util import forest 8 | 9 | def random_bytes(length): 10 | return ''.join(chr(random.randrange(2**8)) for i in xrange(length)) 11 | 12 | class Test(unittest.TestCase): 13 | def test_hashlink1(self): 14 | for i in xrange(100): 15 | d = random_bytes(random.randrange(2048)) 16 | x = data.prefix_to_hash_link(d) 17 | assert data.check_hash_link(x, '') == bitcoin_data.hash256(d) 18 | 19 | def test_hashlink2(self): 20 | for i in xrange(100): 21 | d = random_bytes(random.randrange(2048)) 22 | d2 = random_bytes(random.randrange(2048)) 23 | x = data.prefix_to_hash_link(d) 24 | assert data.check_hash_link(x, d2) == bitcoin_data.hash256(d + d2) 25 | 26 | def test_hashlink3(self): 27 | for i in xrange(100): 28 | d = random_bytes(random.randrange(2048)) 29 | d2 = random_bytes(random.randrange(200)) 30 | d3 = random_bytes(random.randrange(2048)) 31 | x = data.prefix_to_hash_link(d + d2, d2) 32 | assert data.check_hash_link(x, d3, d2) == bitcoin_data.hash256(d + d2 + d3) 33 | 34 | def test_skiplist(self): 35 | t = forest.Tracker() 36 | d = data.WeightsSkipList(t) 37 | for i in xrange(200): 38 | t.add(test_forest.FakeShare(hash=i, previous_hash=i - 1 if i > 0 else None, new_script=i, share_data=dict(donation=1234), target=2**249)) 39 | for i in xrange(200): 40 | a = random.randrange(200) 41 | d(a, random.randrange(a + 1), 1000000*65535)[1] 42 | -------------------------------------------------------------------------------- /p2pool/test/test_p2p.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from twisted.internet import defer, endpoints, protocol, reactor 4 | from twisted.trial import unittest 5 | 6 | from p2pool import networks, p2p 7 | from p2pool.bitcoin import data as bitcoin_data 8 | from p2pool.util import deferral 9 | 10 | 11 | class Test(unittest.TestCase): 12 | @defer.inlineCallbacks 13 | def test_sharereq(self): 14 | class MyNode(p2p.Node): 15 | def __init__(self, df): 16 | p2p.Node.__init__(self, lambda: None, 29333, networks.nets['bitcoin'], {}, set([('127.0.0.1', 9333)]), 0, 0, 0, 0) 17 | 18 | self.df = df 19 | 20 | def handle_share_hashes(self, hashes, peer): 21 | peer.get_shares( 22 | hashes=[hashes[0]], 23 | parents=5, 24 | stops=[], 25 | ).chainDeferred(self.df) 26 | 27 | df = defer.Deferred() 28 | n = MyNode(df) 29 | n.start() 30 | try: 31 | yield df 32 | finally: 33 | yield n.stop() 34 | 35 | @defer.inlineCallbacks 36 | def test_tx_limit(self): 37 | class MyNode(p2p.Node): 38 | def __init__(self, df): 39 | p2p.Node.__init__(self, lambda: None, 29333, networks.nets['bitcoin'], {}, set([('127.0.0.1', 9333)]), 0, 0, 0, 0) 40 | 41 | self.df = df 42 | self.sent_time = 0 43 | 44 | @defer.inlineCallbacks 45 | def got_conn(self, conn): 46 | p2p.Node.got_conn(self, conn) 47 | 48 | yield deferral.sleep(.5) 49 | 50 | new_mining_txs = dict(self.mining_txs_var.value) 51 | for i in xrange(3): 52 | huge_tx = dict( 53 | version=0, 54 | tx_ins=[], 55 | tx_outs=[dict( 56 | value=0, 57 | script='x'*900000, 58 | )], 59 | lock_time=i, 60 | ) 61 | new_mining_txs[bitcoin_data.get_txid(huge_tx)] = huge_tx 62 | self.mining_txs_var.set(new_mining_txs) 63 | 64 | self.sent_time = reactor.seconds() 65 | 66 | def lost_conn(self, conn, reason): 67 | self.df.callback(None) 68 | try: 69 | p2p.Protocol.max_remembered_txs_size *= 10 70 | 71 | df = defer.Deferred() 72 | n = MyNode(df) 73 | n.start() 74 | yield df 75 | if not (n.sent_time <= reactor.seconds() <= n.sent_time + 1): 76 | raise ValueError('node did not disconnect within 1 seconds of receiving too much tx data') 77 | yield n.stop() 78 | finally: 79 | p2p.Protocol.max_remembered_txs_size //= 10 80 | -------------------------------------------------------------------------------- /p2pool/test/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtoomim/p2pool/ece15b03fc2367d02e730b2b85b666217fa8985e/p2pool/test/util/__init__.py -------------------------------------------------------------------------------- /p2pool/test/util/test_datachunker.py: -------------------------------------------------------------------------------- 1 | import random 2 | import unittest 3 | 4 | from p2pool.util import datachunker 5 | 6 | def random_bytes(length): 7 | return ''.join(chr(random.randrange(2**8)) for i in xrange(length)) 8 | 9 | class Test(unittest.TestCase): 10 | def test_stringbuffer(self): 11 | for i in xrange(100): 12 | sb = datachunker.StringBuffer() 13 | 14 | r = random_bytes(random.randrange(1000)) 15 | 16 | amount_inserted = 0 17 | while amount_inserted < len(r): 18 | x = random.randrange(10) 19 | sb.add(r[amount_inserted:amount_inserted+x]) 20 | amount_inserted += x 21 | 22 | amount_removed = 0 23 | while amount_removed < len(r): 24 | x = random.randrange(min(10, len(r) - amount_removed) + 1) 25 | this = sb.get(x) 26 | assert r[amount_removed:amount_removed+x] == this 27 | amount_removed += x 28 | -------------------------------------------------------------------------------- /p2pool/test/util/test_deferral.py: -------------------------------------------------------------------------------- 1 | import random 2 | import time 3 | 4 | from twisted.internet import defer 5 | from twisted.trial import unittest 6 | 7 | from p2pool.util import deferral 8 | 9 | class Test(unittest.TestCase): 10 | @defer.inlineCallbacks 11 | def test_sleep(self): 12 | for i in xrange(10): 13 | length = random.expovariate(1/0.1) 14 | start = time.time() 15 | yield deferral.sleep(length) 16 | end = time.time() 17 | assert length <= end - start <= length + 0.1 18 | -------------------------------------------------------------------------------- /p2pool/test/util/test_expiring_dict.py: -------------------------------------------------------------------------------- 1 | from twisted.internet import defer 2 | from twisted.trial import unittest 3 | 4 | from p2pool.util import deferral, expiring_dict 5 | 6 | class Test(unittest.TestCase): 7 | @defer.inlineCallbacks 8 | def test_expiring_dict1(self): 9 | e = expiring_dict.ExpiringDict(3, get_touches=True) 10 | e[1] = 2 11 | yield deferral.sleep(1.5) 12 | assert 1 in e 13 | yield deferral.sleep(3) 14 | assert 1 not in e 15 | 16 | @defer.inlineCallbacks 17 | def test_expiring_dict2(self): 18 | e = expiring_dict.ExpiringDict(3, get_touches=True) 19 | e[1] = 2 20 | yield deferral.sleep(2.25) 21 | e[1] 22 | yield deferral.sleep(2.25) 23 | assert 1 in e 24 | 25 | @defer.inlineCallbacks 26 | def test_expiring_dict3(self): 27 | e = expiring_dict.ExpiringDict(3, get_touches=False) 28 | e[1] = 2 29 | yield deferral.sleep(2.25) 30 | e[1] 31 | yield deferral.sleep(2.25) 32 | assert 1 not in e 33 | -------------------------------------------------------------------------------- /p2pool/test/util/test_graph.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from p2pool.util import graph 4 | 5 | class Test(unittest.TestCase): 6 | def test_keep_largest(self): 7 | b = dict(a=1, b=3, c=5, d=7, e=9) 8 | assert graph.keep_largest(3, 'squashed')(b) == {'squashed': 9, 'd': 7, 'e': 9} 9 | assert graph.keep_largest(3)(b) == {'c': 5, 'd': 7, 'e': 9} 10 | -------------------------------------------------------------------------------- /p2pool/test/util/test_math.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import random 4 | import unittest 5 | 6 | from p2pool.util import math 7 | 8 | def generate_alphabet(): 9 | if random.randrange(2): 10 | return None 11 | else: 12 | a = map(chr, xrange(256)) 13 | random.shuffle(a) 14 | return a[:random.randrange(2, len(a))] 15 | 16 | class Test(unittest.TestCase): 17 | def test_add_tuples(self): 18 | assert math.add_tuples((1, 2, 3), (4, 5, 6)) == (5, 7, 9) 19 | 20 | def test_bases(self): 21 | for i in xrange(10): 22 | alphabet = generate_alphabet() 23 | for i in xrange(100): 24 | n = random.choice([ 25 | random.randrange(3), 26 | random.randrange(300), 27 | random.randrange(100000000000000000000000000000), 28 | ]) 29 | s = math.natural_to_string(n, alphabet) 30 | n2 = math.string_to_natural(s, alphabet) 31 | #print n, s.encode('hex'), n2 32 | self.assertEquals(n, n2) 33 | 34 | def test_binom(self): 35 | for n in xrange(1, 100): 36 | for x in xrange(n + 1): 37 | left, right = math.binomial_conf_interval(x, n) 38 | assert 0 <= left <= x/n <= right <= 1, (left, right, x, n) 39 | 40 | def test_convertbits(self): 41 | self.assertListEqual([0], math.convertbits([0], 8, 16, True)) 42 | self.assertEqual(None, math.convertbits([0], 8, 16, False)) 43 | self.assertListEqual([0], math.convertbits([0, 0], 8, 16, False)) 44 | self.assertListEqual([0, 0], math.convertbits([0], 16, 8, False)) 45 | self.assertListEqual([255], math.convertbits([0, 255], 8, 16, False)) 46 | self.assertListEqual([65280], math.convertbits([255], 8, 16, True)) 47 | self.assertListEqual([65535], math.convertbits([255, 255], 8, 16, True)) 48 | self.assertListEqual([0, 255], math.convertbits([255], 16, 8, False)) 49 | self.assertListEqual([255, 0], math.convertbits([65280], 16, 8, False)) 50 | self.assertListEqual([255, 255], math.convertbits([65535], 16, 8, False)) 51 | self.assertListEqual([4, 11, 13, 2, 16], math.convertbits([34, 218, 40], 8, 5, True)) 52 | self.assertListEqual([34, 218, 40, 0], math.convertbits([4, 11, 13, 2, 16], 5, 8, True)) 53 | self.assertListEqual([34, 218, 40], math.convertbits([4, 11, 13, 2, 16], 5, 8, False)) 54 | self.assertListEqual([3, 82, 34, 218, 40], math.convertbits([0, 13, 9, 2, 5, 22, 17, 8], 5, 8, True)) 55 | self.assertListEqual([3, 82, 34, 218, 40], math.convertbits([0, 13, 9, 2, 5, 22, 17, 8], 5, 8, False)) 56 | self.assertListEqual([0, 13, 9, 2, 5, 22, 17, 8], math.convertbits([3, 82, 34, 218, 40], 8, 5, True)) 57 | self.assertListEqual([0, 13, 9, 2, 5, 22, 17, 8], math.convertbits([3, 82, 34, 218, 40], 8, 5, False)) 58 | -------------------------------------------------------------------------------- /p2pool/test/util/test_pack.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from p2pool.util import pack 4 | 5 | class Test(unittest.TestCase): 6 | def test_VarInt(self): 7 | t = pack.VarIntType() 8 | for i in xrange(2**20): 9 | assert t.unpack(t.pack(i)) == i 10 | for i in xrange(2**36, 2**36+25): 11 | assert t.unpack(t.pack(i)) == i 12 | -------------------------------------------------------------------------------- /p2pool/test/util/test_skiplist.py: -------------------------------------------------------------------------------- 1 | from p2pool.util import skiplist 2 | 3 | class NotSkipList(object): 4 | def __call__(self, start, *args): 5 | pos = start 6 | sol = self.initial_solution(start, args) 7 | while True: 8 | decision = self.judge(sol, args) 9 | if decision > 0: 10 | raise AssertionError() 11 | elif decision == 0: 12 | return self.finalize(sol) 13 | 14 | delta = self.get_delta(pos) 15 | sol = self.apply_delta(sol, delta, args) 16 | 17 | pos = self.previous(pos) 18 | 19 | def finalize(self, sol): 20 | return sol 21 | 22 | skiplist.SkipList 23 | -------------------------------------------------------------------------------- /p2pool/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtoomim/p2pool/ece15b03fc2367d02e730b2b85b666217fa8985e/p2pool/util/__init__.py -------------------------------------------------------------------------------- /p2pool/util/datachunker.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | class StringBuffer(object): 4 | 'Buffer manager with great worst-case behavior' 5 | 6 | def __init__(self, data=''): 7 | self.buf = collections.deque([data]) 8 | self.buf_len = len(data) 9 | self.pos = 0 10 | 11 | def __len__(self): 12 | return self.buf_len - self.pos 13 | 14 | def add(self, data): 15 | self.buf.append(data) 16 | self.buf_len += len(data) 17 | 18 | def get(self, wants): 19 | if self.buf_len - self.pos < wants: 20 | raise IndexError('not enough data') 21 | data = [] 22 | while wants: 23 | seg = self.buf[0][self.pos:self.pos+wants] 24 | self.pos += len(seg) 25 | while self.buf and self.pos >= len(self.buf[0]): 26 | x = self.buf.popleft() 27 | self.buf_len -= len(x) 28 | self.pos -= len(x) 29 | 30 | data.append(seg) 31 | wants -= len(seg) 32 | return ''.join(data) 33 | 34 | def _DataChunker(receiver): 35 | wants = receiver.next() 36 | buf = StringBuffer() 37 | 38 | while True: 39 | if len(buf) >= wants: 40 | wants = receiver.send(buf.get(wants)) 41 | else: 42 | buf.add((yield)) 43 | def DataChunker(receiver): 44 | ''' 45 | Produces a function that accepts data that is input into a generator 46 | (receiver) in response to the receiver yielding the size of data to wait on 47 | ''' 48 | x = _DataChunker(receiver) 49 | x.next() 50 | return x.send 51 | -------------------------------------------------------------------------------- /p2pool/util/deferred_resource.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | from twisted.internet import defer 4 | from twisted.web import resource, server 5 | from twisted.python import log 6 | 7 | class DeferredResource(resource.Resource): 8 | def render(self, request): 9 | def finish(x): 10 | if request.channel is None: # disconnected 11 | return 12 | if x is not None: 13 | request.write(x) 14 | request.finish() 15 | 16 | def finish_error(fail): 17 | if request.channel is None: # disconnected 18 | return 19 | request.setResponseCode(500) # won't do anything if already written to 20 | request.write('---ERROR---') 21 | request.finish() 22 | log.err(fail, "Error in DeferredResource handler:") 23 | 24 | defer.maybeDeferred(resource.Resource.render, self, request).addCallbacks(finish, finish_error) 25 | return server.NOT_DONE_YET 26 | -------------------------------------------------------------------------------- /p2pool/util/expiring_dict.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import time 4 | import weakref 5 | 6 | from p2pool.util import deferral 7 | 8 | class Node(object): 9 | def __init__(self, contents, prev=None, next=None): 10 | self.contents, self.prev, self.next = contents, prev, next 11 | 12 | def insert_before(self, contents): 13 | self.prev.next = self.prev = node = Node(contents, self.prev, self) 14 | return node 15 | 16 | def insert_after(self, contents): 17 | self.next.prev = self.next = node = Node(contents, self, self.next) 18 | return node 19 | 20 | @staticmethod 21 | def connect(prev, next): 22 | if prev.next is not None or next.prev is not None: 23 | raise ValueError('node already connected') 24 | prev.next, next.prev = next, prev 25 | 26 | def replace(self, contents): 27 | self.contents = contents 28 | 29 | def delete(self): 30 | if self.prev.next is None or self.next.prev is None: 31 | raise ValueError('node not connected') 32 | self.prev.next, self.next.prev = self.next, self.prev 33 | self.next = self.prev = None 34 | 35 | 36 | class LinkedList(object): 37 | def __init__(self, iterable=[]): 38 | self.start, self.end = Node(None), Node(None) 39 | Node.connect(self.start, self.end) 40 | 41 | for item in iterable: 42 | self.append(item) 43 | 44 | def __repr__(self): 45 | return 'LinkedList(%r)' % (list(self),) 46 | 47 | def __len__(self): 48 | return sum(1 for x in self) 49 | 50 | def __iter__(self): 51 | cur = self.start.next 52 | while cur is not self.end: 53 | cur2 = cur 54 | cur = cur.next 55 | yield cur2 # in case cur is deleted, but items inserted after are ignored 56 | 57 | def __reversed__(self): 58 | cur = self.end.prev 59 | while cur is not self.start: 60 | cur2 = cur 61 | cur = cur.prev 62 | yield cur2 63 | 64 | def __getitem__(self, index): 65 | if index < 0: 66 | cur = self.end 67 | for i in xrange(-index): 68 | cur = cur.prev 69 | if cur is self.start: 70 | raise IndexError('index out of range') 71 | else: 72 | cur = self.start 73 | for i in xrange(index + 1): 74 | cur = cur.next 75 | if cur is self.end: 76 | raise IndexError('index out of range') 77 | return cur 78 | 79 | def appendleft(self, item): 80 | return self.start.insert_after(item) 81 | 82 | def append(self, item): 83 | return self.end.insert_before(item) 84 | 85 | def popleft(self): 86 | node = self.start.next 87 | if node is self.end: 88 | raise IndexError('popleft from empty') 89 | node.delete() 90 | return node.contents 91 | 92 | def pop(self): 93 | node = self.end.prev 94 | if node is self.start: 95 | raise IndexError('pop from empty') 96 | node.delete() 97 | return node.contents 98 | 99 | 100 | class ExpiringDict(object): 101 | def __init__(self, expiry_time, get_touches=True): 102 | self.expiry_time = expiry_time 103 | self.get_touches = get_touches 104 | 105 | self.expiry_deque = LinkedList() 106 | self.d = dict() # key -> node, value 107 | 108 | self_ref = weakref.ref(self, lambda _: expire_loop.stop() if expire_loop.running else None) 109 | self._expire_loop = expire_loop = deferral.RobustLoopingCall(lambda: self_ref().expire() if isinstance(self_ref(),ExpiringDict) else None) 110 | expire_loop.start(1) 111 | 112 | def stop(self): 113 | self._expire_loop.stop() 114 | 115 | def __repr__(self): 116 | return 'ExpiringDict' + repr(self.__dict__) 117 | 118 | def __len__(self): 119 | return len(self.d) 120 | 121 | _nothing = object() 122 | def touch(self, key, value=_nothing): 123 | 'Updates expiry node, optionally replacing value, returning new value' 124 | if value is self._nothing or key in self.d: 125 | node, old_value = self.d[key] 126 | node.delete() 127 | 128 | new_value = old_value if value is self._nothing else value 129 | self.d[key] = self.expiry_deque.append((time.time() + self.expiry_time, key)), new_value 130 | return new_value 131 | 132 | def expire(self): 133 | t = time.time() 134 | for node in self.expiry_deque: 135 | timestamp, key = node.contents 136 | if timestamp > t: 137 | break 138 | del self.d[key] 139 | node.delete() 140 | 141 | def __contains__(self, key): 142 | return key in self.d 143 | 144 | def __getitem__(self, key): 145 | if self.get_touches: 146 | value = self.touch(key) 147 | else: 148 | node, value = self.d[key] 149 | return value 150 | 151 | def __setitem__(self, key, value): 152 | self.touch(key, value) 153 | 154 | def __delitem__(self, key): 155 | node, value = self.d.pop(key) 156 | node.delete() 157 | 158 | def get(self, key, default_value=None): 159 | if key in self.d: 160 | res = self[key] 161 | else: 162 | res = default_value 163 | return res 164 | 165 | def setdefault(self, key, default_value): 166 | if key in self.d: 167 | return self[key] 168 | else: 169 | self[key] = default_value 170 | return default_value 171 | 172 | def keys(self): 173 | return self.d.keys() 174 | 175 | def values(self): 176 | return [value for node, value in self.d.itervalues()] 177 | 178 | def itervalues(self): 179 | for node, value in self.d.itervalues(): 180 | yield value 181 | -------------------------------------------------------------------------------- /p2pool/util/fixargparse.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import argparse 4 | import sys 5 | 6 | 7 | class FixedArgumentParser(argparse.ArgumentParser): 8 | ''' 9 | fixes argparse's handling of empty string arguments 10 | and changes @filename behaviour to accept multiple arguments on each line 11 | ''' 12 | 13 | def _read_args_from_files(self, arg_strings): 14 | # expand arguments referencing files 15 | new_arg_strings = [] 16 | for arg_string in arg_strings: 17 | 18 | # for regular arguments, just add them back into the list 19 | if not arg_string or arg_string[0] not in self.fromfile_prefix_chars: 20 | new_arg_strings.append(arg_string) 21 | 22 | # replace arguments referencing files with the file content 23 | else: 24 | try: 25 | args_file = open(arg_string[1:]) 26 | try: 27 | arg_strings = [] 28 | for arg_line in args_file.read().splitlines(): 29 | for arg in self.convert_arg_line_to_args(arg_line): 30 | arg_strings.append(arg) 31 | arg_strings = self._read_args_from_files(arg_strings) 32 | new_arg_strings.extend(arg_strings) 33 | finally: 34 | args_file.close() 35 | except IOError: 36 | err = sys.exc_info()[1] 37 | self.error(str(err)) 38 | 39 | # return the modified argument list 40 | return new_arg_strings 41 | 42 | def convert_arg_line_to_args(self, arg_line): 43 | return [arg for arg in arg_line.split() if arg.strip()] 44 | -------------------------------------------------------------------------------- /p2pool/util/jsonrpc.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import json 4 | import weakref 5 | 6 | from twisted.internet import defer 7 | from twisted.protocols import basic 8 | from twisted.python import failure, log 9 | from twisted.web import client, error 10 | 11 | from p2pool.util import deferral, deferred_resource, memoize 12 | 13 | class Error(Exception): 14 | def __init__(self, code, message, data=None): 15 | if type(self) is Error: 16 | raise TypeError("can't directly instantiate Error class; use Error_for_code") 17 | if not isinstance(code, int): 18 | raise TypeError('code must be an int') 19 | #if not isinstance(message, unicode): 20 | # raise TypeError('message must be a unicode') 21 | self.code, self.message, self.data = code, message, data 22 | def __str__(self): 23 | return '%i %s' % (self.code, self.message) + (' %r' % (self.data, ) if self.data is not None else '') 24 | def _to_obj(self): 25 | return { 26 | 'code': self.code, 27 | 'message': self.message, 28 | 'data': self.data, 29 | } 30 | 31 | @memoize.memoize_with_backing(weakref.WeakValueDictionary()) 32 | def Error_for_code(code): 33 | class NarrowError(Error): 34 | def __init__(self, *args, **kwargs): 35 | Error.__init__(self, code, *args, **kwargs) 36 | return NarrowError 37 | 38 | 39 | class Proxy(object): 40 | def __init__(self, func, services=[]): 41 | self._func = func 42 | self._services = services 43 | 44 | def __getattr__(self, attr): 45 | if attr.startswith('rpc_'): 46 | return lambda *params: self._func('.'.join(self._services + [attr[len('rpc_'):]]), params) 47 | elif attr.startswith('svc_'): 48 | return Proxy(self._func, self._services + [attr[len('svc_'):]]) 49 | else: 50 | raise AttributeError('%r object has no attribute %r' % (self.__class__.__name__, attr)) 51 | 52 | @defer.inlineCallbacks 53 | def _handle(data, provider, preargs=(), response_handler=None): 54 | id_ = None 55 | 56 | try: 57 | try: 58 | try: 59 | req = json.loads(data) 60 | except Exception: 61 | raise Error_for_code(-32700)(u'Parse error') 62 | 63 | if 'result' in req or 'error' in req: 64 | response_handler(req['id'], req['result'] if 'error' not in req or req['error'] is None else 65 | failure.Failure(Error_for_code(req['error']['code'])(req['error']['message'], req['error'].get('data', None)))) 66 | defer.returnValue(None) 67 | 68 | id_ = req.get('id', None) 69 | method = req.get('method', None) 70 | if not isinstance(method, basestring): 71 | raise Error_for_code(-32600)(u'Invalid Request') 72 | params = req.get('params', []) 73 | if not isinstance(params, list): 74 | raise Error_for_code(-32600)(u'Invalid Request') 75 | 76 | for service_name in method.split('.')[:-1]: 77 | provider = getattr(provider, 'svc_' + service_name, None) 78 | if provider is None: 79 | raise Error_for_code(-32601)(u'Service not found') 80 | 81 | method_meth = getattr(provider, 'rpc_' + method.split('.')[-1], None) 82 | if method_meth is None: 83 | raise Error_for_code(-32601)(u'Method not found') 84 | 85 | result = yield method_meth(*list(preargs) + list(params)) 86 | error = None 87 | except Error: 88 | raise 89 | except Exception: 90 | log.err(None, 'Squelched JSON error:') 91 | raise Error_for_code(-32099)(u'Unknown error') 92 | except Error, e: 93 | result = None 94 | error = e._to_obj() 95 | 96 | defer.returnValue(json.dumps(dict( 97 | jsonrpc='2.0', 98 | id=id_, 99 | result=result, 100 | error=error, 101 | ))) 102 | 103 | # HTTP 104 | 105 | @defer.inlineCallbacks 106 | def _http_do(url, headers, timeout, method, params): 107 | id_ = 0 108 | 109 | try: 110 | data = yield client.getPage( 111 | url=url, 112 | method='POST', 113 | headers=dict(headers, **{'Content-Type': 'application/json'}), 114 | postdata=json.dumps({ 115 | 'jsonrpc': '2.0', 116 | 'method': method, 117 | 'params': params, 118 | 'id': id_, 119 | }), 120 | timeout=timeout, 121 | ) 122 | except error.Error, e: 123 | try: 124 | resp = json.loads(e.response) 125 | except: 126 | raise e 127 | else: 128 | resp = json.loads(data) 129 | 130 | if resp['id'] != id_: 131 | raise ValueError('invalid id') 132 | if 'error' in resp and resp['error'] is not None: 133 | raise Error_for_code(resp['error']['code'])(resp['error']['message'], resp['error'].get('data', None)) 134 | defer.returnValue(resp['result']) 135 | HTTPProxy = lambda url, headers={}, timeout=5: Proxy(lambda method, params: _http_do(url, headers, timeout, method, params)) 136 | 137 | class HTTPServer(deferred_resource.DeferredResource): 138 | def __init__(self, provider): 139 | deferred_resource.DeferredResource.__init__(self) 140 | self._provider = provider 141 | 142 | @defer.inlineCallbacks 143 | def render_POST(self, request): 144 | data = yield _handle(request.content.read(), self._provider, preargs=[request]) 145 | assert data is not None 146 | request.setHeader('Content-Type', 'application/json') 147 | request.setHeader('Content-Length', len(data)) 148 | request.write(data) 149 | 150 | class LineBasedPeer(basic.LineOnlyReceiver): 151 | delimiter = '\n' 152 | 153 | def __init__(self): 154 | #basic.LineOnlyReceiver.__init__(self) 155 | self._matcher = deferral.GenericDeferrer(max_id=2**30, func=lambda id, method, params: self.sendLine(json.dumps({ 156 | 'jsonrpc': '2.0', 157 | 'method': method, 158 | 'params': params, 159 | 'id': id, 160 | }))) 161 | self.other = Proxy(self._matcher) 162 | 163 | def lineReceived(self, line): 164 | _handle(line, self, response_handler=self._matcher.got_response).addCallback(lambda line2: self.sendLine(line2) if line2 is not None else None) 165 | -------------------------------------------------------------------------------- /p2pool/util/logging.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import datetime 3 | import os 4 | import sys 5 | 6 | from twisted.python import log 7 | 8 | class EncodeReplacerPipe(object): 9 | def __init__(self, inner_file): 10 | self.inner_file = inner_file 11 | self.softspace = 0 12 | def write(self, data): 13 | if isinstance(data, unicode): 14 | try: 15 | data = data.encode(self.inner_file.encoding, 'replace') 16 | except: 17 | data = data.encode('ascii', 'replace') 18 | self.inner_file.write(data) 19 | def flush(self): 20 | self.inner_file.flush() 21 | 22 | class LogFile(object): 23 | def __init__(self, filename): 24 | self.filename = filename 25 | self.inner_file = None 26 | self.reopen() 27 | def reopen(self): 28 | if self.inner_file is not None: 29 | self.inner_file.close() 30 | open(self.filename, 'a').close() 31 | f = open(self.filename, 'rb') 32 | f.seek(0, os.SEEK_END) 33 | length = f.tell() 34 | if length > 100*1000*1000: 35 | f.seek(-1000*1000, os.SEEK_END) 36 | while True: 37 | if f.read(1) in ('', '\n'): 38 | break 39 | data = f.read() 40 | f.close() 41 | f = open(self.filename, 'wb') 42 | f.write(data) 43 | f.close() 44 | self.inner_file = codecs.open(self.filename, 'a', 'utf-8') 45 | def write(self, data): 46 | self.inner_file.write(data) 47 | def flush(self): 48 | self.inner_file.flush() 49 | 50 | class TeePipe(object): 51 | def __init__(self, outputs): 52 | self.outputs = outputs 53 | def write(self, data): 54 | for output in self.outputs: 55 | output.write(data) 56 | def flush(self): 57 | for output in self.outputs: 58 | output.flush() 59 | 60 | class TimestampingPipe(object): 61 | def __init__(self, inner_file): 62 | self.inner_file = inner_file 63 | self.buf = '' 64 | self.softspace = 0 65 | def write(self, data): 66 | buf = self.buf + data 67 | lines = buf.split('\n') 68 | for line in lines[:-1]: 69 | self.inner_file.write('%s %s\n' % (datetime.datetime.now(), line)) 70 | self.inner_file.flush() 71 | self.buf = lines[-1] 72 | def flush(self): 73 | pass 74 | 75 | class AbortPipe(object): 76 | def __init__(self, inner_file): 77 | self.inner_file = inner_file 78 | self.softspace = 0 79 | def write(self, data): 80 | try: 81 | self.inner_file.write(data) 82 | except: 83 | sys.stdout = sys.__stdout__ 84 | log.DefaultObserver.stderr = sys.stderr = sys.__stderr__ 85 | raise 86 | def flush(self): 87 | self.inner_file.flush() 88 | 89 | class PrefixPipe(object): 90 | def __init__(self, inner_file, prefix): 91 | self.inner_file = inner_file 92 | self.prefix = prefix 93 | self.buf = '' 94 | self.softspace = 0 95 | def write(self, data): 96 | buf = self.buf + data 97 | lines = buf.split('\n') 98 | for line in lines[:-1]: 99 | self.inner_file.write(self.prefix + line + '\n') 100 | self.inner_file.flush() 101 | self.buf = lines[-1] 102 | def flush(self): 103 | pass 104 | -------------------------------------------------------------------------------- /p2pool/util/memoize.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | 3 | class LRUDict(object): 4 | def __init__(self, n): 5 | self.n = n 6 | self.inner = {} 7 | self.counter = itertools.count() 8 | def get(self, key, default=None): 9 | if key in self.inner: 10 | x, value = self.inner[key] 11 | self.inner[key] = self.counter.next(), value 12 | return value 13 | return default 14 | def __setitem__(self, key, value): 15 | self.inner[key] = self.counter.next(), value 16 | while len(self.inner) > self.n: 17 | self.inner.pop(min(self.inner, key=lambda k: self.inner[k][0])) 18 | 19 | _nothing = object() 20 | 21 | def memoize_with_backing(backing, has_inverses=set()): 22 | def a(f): 23 | def b(*args): 24 | res = backing.get((f, args), _nothing) 25 | if res is not _nothing: 26 | return res 27 | 28 | res = f(*args) 29 | 30 | backing[(f, args)] = res 31 | for inverse in has_inverses: 32 | backing[(inverse, args[:-1] + (res,))] = args[-1] 33 | 34 | return res 35 | return b 36 | return a 37 | 38 | def memoize(f): 39 | return memoize_with_backing({})(f) 40 | 41 | 42 | class cdict(dict): 43 | def __init__(self, func): 44 | dict.__init__(self) 45 | self._func = func 46 | 47 | def __missing__(self, key): 48 | value = self._func(key) 49 | self[key] = value 50 | return value 51 | 52 | def fast_memoize_single_arg(func): 53 | return cdict(func).__getitem__ 54 | 55 | class cdict2(dict): 56 | def __init__(self, func): 57 | dict.__init__(self) 58 | self._func = func 59 | 60 | def __missing__(self, key): 61 | value = self._func(*key) 62 | self[key] = value 63 | return value 64 | 65 | def fast_memoize_multiple_args(func): 66 | f = cdict2(func).__getitem__ 67 | return lambda *args: f(args) 68 | -------------------------------------------------------------------------------- /p2pool/util/memory.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | _scale = {'kB': 1024, 'mB': 1024*1024, 5 | 'KB': 1024, 'MB': 1024*1024} 6 | 7 | def resident(): 8 | if platform.system() == 'Windows': 9 | from wmi import WMI 10 | w = WMI('.') 11 | result = w.query("SELECT WorkingSet FROM Win32_PerfRawData_PerfProc_Process WHERE IDProcess=%d" % os.getpid()) 12 | return int(result[0].WorkingSet) 13 | else: 14 | with open('/proc/%d/status' % os.getpid()) as f: 15 | v = f.read() 16 | i = v.index('VmRSS:') 17 | v = v[i:].split(None, 3) 18 | #assert len(v) == 3, v 19 | return float(v[1]) * _scale[v[2]] 20 | -------------------------------------------------------------------------------- /p2pool/util/p2protocol.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Generic message-based protocol used by Bitcoin and P2Pool for P2P communication 3 | ''' 4 | 5 | import hashlib 6 | import struct 7 | 8 | from twisted.internet import protocol 9 | from twisted.python import log 10 | 11 | import p2pool 12 | from p2pool.util import datachunker, variable 13 | 14 | class TooLong(Exception): 15 | pass 16 | 17 | class Protocol(protocol.Protocol): 18 | def __init__(self, message_prefix, max_payload_length, traffic_happened=variable.Event(), ignore_trailing_payload=False): 19 | self._message_prefix = message_prefix 20 | self._max_payload_length = max_payload_length 21 | self.dataReceived2 = datachunker.DataChunker(self.dataReceiver()) 22 | self.traffic_happened = traffic_happened 23 | self.ignore_trailing_payload = ignore_trailing_payload 24 | 25 | def dataReceived(self, data): 26 | self.traffic_happened.happened('p2p/in', len(data)) 27 | self.dataReceived2(data) 28 | 29 | def dataReceiver(self): 30 | while True: 31 | start = '' 32 | while start != self._message_prefix: 33 | start = (start + (yield 1))[-len(self._message_prefix):] 34 | 35 | command = (yield 12).rstrip('\0') 36 | length, = struct.unpack(' self._max_payload_length: 38 | print 'length too large' 39 | continue 40 | checksum = yield 4 41 | payload = yield length 42 | 43 | if hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4] != checksum: 44 | print 'invalid hash for', self.transport.getPeer().host, repr(command), length, checksum.encode('hex') 45 | if p2pool.DEBUG: 46 | print hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4].encode('hex'), payload.encode('hex') 47 | self.badPeerHappened() 48 | continue 49 | 50 | type_ = getattr(self, 'message_' + command, None) 51 | if type_ is None: 52 | if p2pool.DEBUG: 53 | print 'no type for', repr(command) 54 | continue 55 | 56 | try: 57 | self.packetReceived(command, type_.unpack(payload, self.ignore_trailing_payload)) 58 | except: 59 | print 'RECV', command, payload[:100].encode('hex') + ('...' if len(payload) > 100 else '') 60 | log.err(None, 'Error handling message: (see RECV line)') 61 | self.disconnect() 62 | 63 | def packetReceived(self, command, payload2): 64 | handler = getattr(self, 'handle_' + command, None) 65 | if handler is None: 66 | if p2pool.DEBUG: 67 | print 'no handler for', repr(command) 68 | return 69 | 70 | if getattr(self, 'connected', True) and not getattr(self, 'disconnecting', False): 71 | handler(**payload2) 72 | 73 | def disconnect(self): 74 | if hasattr(self.transport, 'abortConnection'): 75 | # Available since Twisted 11.1 76 | self.transport.abortConnection() 77 | else: 78 | # This doesn't always close timed out connections! warned about in main 79 | self.transport.loseConnection() 80 | 81 | def badPeerHappened(self): 82 | self.disconnect() 83 | 84 | def sendPacket(self, command, payload2): 85 | if len(command) >= 12: 86 | raise ValueError('command too long') 87 | type_ = getattr(self, 'message_' + command, None) 88 | if type_ is None: 89 | raise ValueError('invalid command') 90 | #print 'SEND', command, repr(payload2)[:500] 91 | payload = type_.pack(payload2) 92 | if len(payload) > self._max_payload_length: 93 | raise TooLong('payload too long') 94 | data = self._message_prefix + struct.pack('<12sI', command, len(payload)) + hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4] + payload 95 | self.traffic_happened.happened('p2p/out', len(data)) 96 | self.transport.write(data) 97 | 98 | def __getattr__(self, attr): 99 | prefix = 'send_' 100 | if attr.startswith(prefix): 101 | command = attr[len(prefix):] 102 | return lambda **payload2: self.sendPacket(command, payload2) 103 | #return protocol.Protocol.__getattr__(self, attr) 104 | raise AttributeError(attr) 105 | -------------------------------------------------------------------------------- /p2pool/util/segwit_addr.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017 Pieter Wuille 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | # THE SOFTWARE. 20 | 21 | """Reference implementation for Bech32 and segwit addresses.""" 22 | 23 | from math import convertbits 24 | 25 | CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" 26 | 27 | 28 | def bech32_polymod(values): 29 | """Internal function that computes the Bech32 checksum.""" 30 | generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] 31 | chk = 1 32 | for value in values: 33 | top = chk >> 25 34 | chk = (chk & 0x1ffffff) << 5 ^ value 35 | for i in range(5): 36 | chk ^= generator[i] if ((top >> i) & 1) else 0 37 | return chk 38 | 39 | 40 | def bech32_hrp_expand(hrp): 41 | """Expand the HRP into values for checksum computation.""" 42 | return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp] 43 | 44 | 45 | def bech32_verify_checksum(hrp, data): 46 | """Verify a checksum given HRP and converted data characters.""" 47 | return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1 48 | 49 | 50 | def bech32_create_checksum(hrp, data): 51 | """Compute the checksum values given HRP and data.""" 52 | values = bech32_hrp_expand(hrp) + data 53 | polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1 54 | return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)] 55 | 56 | 57 | def bech32_encode(hrp, data): 58 | """Compute a Bech32 string given HRP and data values.""" 59 | combined = data + bech32_create_checksum(hrp, data) 60 | return hrp + '1' + ''.join([CHARSET[d] for d in combined]) 61 | 62 | 63 | def bech32_decode(bech): 64 | """Validate a Bech32 string, and determine HRP and data.""" 65 | if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or 66 | (bech.lower() != bech and bech.upper() != bech)): 67 | return (None, None) 68 | bech = bech.lower() 69 | pos = bech.rfind('1') 70 | if pos < 1 or pos + 7 > len(bech) or len(bech) > 90: 71 | return (None, None) 72 | if not all(x in CHARSET for x in bech[pos+1:]): 73 | return (None, None) 74 | hrp = bech[:pos] 75 | data = [CHARSET.find(x) for x in bech[pos+1:]] 76 | if not bech32_verify_checksum(hrp, data): 77 | return (None, None) 78 | return (hrp, data[:-6]) 79 | 80 | def decode(hrp, addr): 81 | """Decode a segwit address.""" 82 | hrpgot, data = bech32_decode(addr) 83 | if hrpgot != hrp: 84 | return (None, None) 85 | decoded = convertbits(data[1:], 5, 8, False) 86 | if decoded is None or len(decoded) < 2 or len(decoded) > 40: 87 | return (None, None) 88 | if data[0] > 16: 89 | return (None, None) 90 | if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32: 91 | return (None, None) 92 | return (data[0], decoded) 93 | 94 | 95 | def encode(hrp, witver, witprog): 96 | """Encode a segwit address.""" 97 | ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5)) 98 | if decode(hrp, ret) == (None, None): 99 | return None 100 | return ret 101 | -------------------------------------------------------------------------------- /p2pool/util/skiplist.py: -------------------------------------------------------------------------------- 1 | from p2pool.util import math, memoize 2 | 3 | class SkipList(object): 4 | def __init__(self, p=0.5): 5 | self.p = p 6 | 7 | self.skips = {} 8 | 9 | def forget_item(self, item): 10 | self.skips.pop(item, None) 11 | 12 | @memoize.memoize_with_backing(memoize.LRUDict(5)) 13 | def __call__(self, start, *args): 14 | updates = {} 15 | pos = start 16 | sol = self.initial_solution(start, args) 17 | if self.judge(sol, args) == 0: 18 | return self.finalize(sol, args) 19 | while True: 20 | if pos not in self.skips: 21 | self.skips[pos] = math.geometric(self.p), [(self.previous(pos), self.get_delta(pos))] 22 | skip_length, skip = self.skips[pos] 23 | 24 | # fill previous updates 25 | for i in xrange(skip_length): 26 | if i in updates: 27 | that_hash, delta = updates.pop(i) 28 | x, y = self.skips[that_hash] 29 | assert len(y) == i 30 | y.append((pos, delta)) 31 | 32 | # put desired skip nodes in updates 33 | for i in xrange(len(skip), skip_length): 34 | updates[i] = pos, None 35 | 36 | #if skip_length + 1 in updates: 37 | # updates[skip_length + 1] = self.combine(updates[skip_length + 1], updates[skip_length]) 38 | 39 | for jump, delta in reversed(skip): 40 | sol_if = self.apply_delta(sol, delta, args) 41 | decision = self.judge(sol_if, args) 42 | #print pos, sol, jump, delta, sol_if, decision 43 | if decision == 0: 44 | return self.finalize(sol_if, args) 45 | elif decision < 0: 46 | sol = sol_if 47 | break 48 | else: 49 | raise AssertionError() 50 | 51 | sol = sol_if 52 | pos = jump 53 | 54 | # XXX could be better by combining updates 55 | for x in updates: 56 | updates[x] = updates[x][0], self.combine_deltas(updates[x][1], delta) if updates[x][1] is not None else delta 57 | 58 | def finalize(self, sol, args): 59 | return sol 60 | -------------------------------------------------------------------------------- /p2pool/util/switchprotocol.py: -------------------------------------------------------------------------------- 1 | from twisted.internet import protocol 2 | 3 | class FirstByteSwitchProtocol(protocol.Protocol): 4 | p = None 5 | def dataReceived(self, data): 6 | if self.p is None: 7 | if not data: return 8 | serverfactory = self.factory.first_byte_to_serverfactory.get(data[0], self.factory.default_serverfactory) 9 | self.p = serverfactory.buildProtocol(self.transport.getPeer()) 10 | self.p.makeConnection(self.transport) 11 | self.p.dataReceived(data) 12 | def connectionLost(self, reason): 13 | if self.p is not None: 14 | self.p.connectionLost(reason) 15 | 16 | class FirstByteSwitchFactory(protocol.ServerFactory): 17 | protocol = FirstByteSwitchProtocol 18 | 19 | def __init__(self, first_byte_to_serverfactory, default_serverfactory): 20 | self.first_byte_to_serverfactory = first_byte_to_serverfactory 21 | self.default_serverfactory = default_serverfactory 22 | 23 | def startFactory(self): 24 | for f in list(self.first_byte_to_serverfactory.values()) + [self.default_serverfactory]: 25 | f.doStart() 26 | 27 | def stopFactory(self): 28 | for f in list(self.first_byte_to_serverfactory.values()) + [self.default_serverfactory]: 29 | f.doStop() 30 | -------------------------------------------------------------------------------- /p2pool/util/variable.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import weakref 3 | 4 | from twisted.internet import defer, reactor 5 | from twisted.python import failure, log 6 | 7 | class Event(object): 8 | def __init__(self): 9 | self.observers = {} 10 | self.id_generator = itertools.count() 11 | self._once = None 12 | self.times = 0 13 | 14 | def run_and_watch(self, func): 15 | func() 16 | return self.watch(func) 17 | def watch_weakref(self, obj, func): 18 | # func must not contain a reference to obj! 19 | watch_id = self.watch(lambda *args: func(obj_ref(), *args)) 20 | obj_ref = weakref.ref(obj, lambda _: self.unwatch(watch_id)) 21 | def watch(self, func): 22 | id = self.id_generator.next() 23 | self.observers[id] = func 24 | return id 25 | def unwatch(self, id): 26 | self.observers.pop(id) 27 | 28 | @property 29 | def once(self): 30 | res = self._once 31 | if res is None: 32 | res = self._once = Event() 33 | return res 34 | 35 | def happened(self, *event): 36 | self.times += 1 37 | 38 | once, self._once = self._once, None 39 | 40 | for id, func in sorted(self.observers.iteritems()): 41 | try: 42 | func(*event) 43 | except: 44 | log.err(None, "Error while processing Event callbacks:") 45 | 46 | if once is not None: 47 | once.happened(*event) 48 | 49 | def get_deferred(self, timeout=None): 50 | once = self.once 51 | df = defer.Deferred() 52 | id1 = once.watch(lambda *event: df.callback(event)) 53 | if timeout is not None: 54 | def do_timeout(): 55 | df.errback(failure.Failure(defer.TimeoutError('in Event.get_deferred'))) 56 | once.unwatch(id1) 57 | once.unwatch(x) 58 | delay = reactor.callLater(timeout, do_timeout) 59 | x = once.watch(lambda *event: delay.cancel()) 60 | return df 61 | 62 | class Variable(object): 63 | def __init__(self, value): 64 | self.value = value 65 | self.changed = Event() 66 | self.transitioned = Event() 67 | 68 | def set(self, value): 69 | if value == self.value: 70 | return 71 | 72 | oldvalue = self.value 73 | self.value = value 74 | self.changed.happened(value) 75 | self.transitioned.happened(oldvalue, value) 76 | 77 | @defer.inlineCallbacks 78 | def get_when_satisfies(self, func): 79 | while True: 80 | if func(self.value): 81 | defer.returnValue(self.value) 82 | yield self.changed.once.get_deferred() 83 | 84 | def get_not_none(self): 85 | return self.get_when_satisfies(lambda val: val is not None) 86 | 87 | class VariableDict(Variable): 88 | def __init__(self, value): 89 | Variable.__init__(self, value) 90 | self.added = Event() 91 | self.removed = Event() 92 | 93 | def add(self, values): 94 | new_items = dict([item for item in values.iteritems() if not item[0] in self.value or self.value[item[0]] != item[1]]) 95 | self.value.update(values) 96 | self.added.happened(new_items) 97 | # XXX call self.changed and self.transitioned 98 | 99 | def remove(self, values): 100 | gone_items = dict([item for item in values.iteritems() if item[0] in self.value]) 101 | for key in gone_keys: 102 | del self.values[key] 103 | self.removed.happened(new_items) 104 | # XXX call self.changed and self.transitioned 105 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Twisted>=12.2.0 2 | argparse>=1.2.1 3 | pyOpenSSL>=0.13 4 | -------------------------------------------------------------------------------- /run_p2pool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from p2pool import main 4 | 5 | main.run() 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import sys 4 | import zipfile 5 | import platform 6 | 7 | from distutils.core import setup 8 | from distutils.sysconfig import get_python_lib 9 | import py2exe 10 | 11 | version = __import__('p2pool').__version__ 12 | im64 = '64' in platform.architecture()[0] 13 | 14 | extra_includes = [] 15 | import p2pool.networks 16 | extra_includes.extend('p2pool.networks.' + x for x in p2pool.networks.nets) 17 | import p2pool.bitcoin.networks 18 | extra_includes.extend('p2pool.bitcoin.networks.' + x for x in p2pool.bitcoin.networks.nets) 19 | 20 | if os.path.exists('INITBAK'): 21 | os.remove('INITBAK') 22 | os.rename(os.path.join('p2pool', '__init__.py'), 'INITBAK') 23 | try: 24 | open(os.path.join('p2pool', '__init__.py'), 'wb').write('__version__ = %r%s%sDEBUG = False%s' % (version, os.linesep, os.linesep, os.linesep)) 25 | mfcdir = get_python_lib() + '\pythonwin\\' 26 | mfcfiles = [os.path.join(mfcdir, i) for i in ["mfc90.dll", "mfc90u.dll", "mfcm90.dll", "mfcm90u.dll", "Microsoft.VC90.MFC.manifest"]] 27 | bundle = 1 28 | if im64: 29 | bundle = bundle + 2 30 | sys.argv[1:] = ['py2exe'] 31 | setup(name='p2pool', 32 | version=version, 33 | description='Peer-to-peer Bitcoin mining pool', 34 | author='Forrest Voight', 35 | author_email='forrest@forre.st', 36 | url='http://p2pool.forre.st/', 37 | data_files=[ 38 | ('', ['README.md']), 39 | ("Microsoft.VC90.MFC", mfcfiles), 40 | ('web-static', [ 41 | 'web-static/d3.v2.min.js', 42 | 'web-static/favicon.ico', 43 | 'web-static/graphs.html', 44 | 'web-static/index.html', 45 | 'web-static/share.html', 46 | ]), 47 | ], 48 | 49 | console=['run_p2pool.py'], 50 | options=dict(py2exe=dict( 51 | bundle_files=bundle, 52 | dll_excludes=['w9xpopen.exe', "mswsock.dll", "MSWSOCK.dll"], 53 | includes=['twisted.web.resource', 'ltc_scrypt'] + extra_includes, 54 | )), 55 | zipfile=None, 56 | ) 57 | finally: 58 | os.remove(os.path.join('p2pool', '__init__.py')) 59 | os.rename('INITBAK', os.path.join('p2pool', '__init__.py')) 60 | 61 | win = '32' 62 | if im64: 63 | win = '64' 64 | 65 | dir_name = 'p2pool_win' + win + '_' + version 66 | 67 | if os.path.exists(dir_name): 68 | shutil.rmtree(dir_name) 69 | os.rename('dist', dir_name) 70 | 71 | with zipfile.ZipFile(dir_name + '.zip', 'w', zipfile.ZIP_DEFLATED) as zf: 72 | for dirpath, dirnames, filenames in os.walk(dir_name): 73 | for filename in filenames: 74 | zf.write(os.path.join(dirpath, filename)) 75 | 76 | print dir_name 77 | -------------------------------------------------------------------------------- /web-static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtoomim/p2pool/ece15b03fc2367d02e730b2b85b666217fa8985e/web-static/favicon.ico -------------------------------------------------------------------------------- /wstools/.cvsignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /wstools/MIMEAttachment.py: -------------------------------------------------------------------------------- 1 | #TODO add the license 2 | #I had to rewrite this class because the python MIME email.mime (version 2.5) 3 | #are buggy, they use \n instead \r\n for new line which is not compliant 4 | #to standard! 5 | # http://bugs.python.org/issue5525 6 | 7 | #TODO do not load all the message in memory stream it from the disk 8 | 9 | import re 10 | import random 11 | import sys 12 | 13 | 14 | #new line 15 | NL='\r\n' 16 | 17 | _width = len(repr(sys.maxint-1)) 18 | _fmt = '%%0%dd' % _width 19 | 20 | class MIMEMessage: 21 | 22 | def __init__(self): 23 | self._files = [] 24 | self._xmlMessage = "" 25 | self._startCID = "" 26 | self._boundary = "" 27 | 28 | def makeBoundary(self): 29 | #create the boundary 30 | msgparts = [] 31 | msgparts.append(self._xmlMessage) 32 | for i in self._files: 33 | msgparts.append(i.read()) 34 | #this sucks, all in memory 35 | alltext = NL.join(msgparts) 36 | self._boundary = _make_boundary(alltext) 37 | #maybe I can save some memory 38 | del alltext 39 | del msgparts 40 | self._startCID = "<" + (_fmt % random.randrange(sys.maxint)) + (_fmt % random.randrange(sys.maxint)) + ">" 41 | 42 | 43 | def toString(self): 44 | '''it return a string with the MIME message''' 45 | if len(self._boundary) == 0: 46 | #the makeBoundary hasn't been called yet 47 | self.makeBoundary() 48 | #ok we have everything let's start to spit the message out 49 | #first the XML 50 | returnstr = NL + "--" + self._boundary + NL 51 | returnstr += "Content-Type: text/xml; charset=\"us-ascii\"" + NL 52 | returnstr += "Content-Transfer-Encoding: 7bit" + NL 53 | returnstr += "Content-Id: " + self._startCID + NL + NL 54 | returnstr += self._xmlMessage + NL 55 | #then the files 56 | for file in self._files: 57 | returnstr += "--" + self._boundary + NL 58 | returnstr += "Content-Type: application/octet-stream" + NL 59 | returnstr += "Content-Transfer-Encoding: binary" + NL 60 | returnstr += "Content-Id: <" + str(id(file)) + ">" + NL + NL 61 | file.seek(0) 62 | returnstr += file.read() + NL 63 | #closing boundary 64 | returnstr += "--" + self._boundary + "--" + NL 65 | return returnstr 66 | 67 | def attachFile(self, file): 68 | ''' 69 | it adds a file to this attachment 70 | ''' 71 | self._files.append(file) 72 | 73 | def addXMLMessage(self, xmlMessage): 74 | ''' 75 | it adds the XML message. we can have only one XML SOAP message 76 | ''' 77 | self._xmlMessage = xmlMessage 78 | 79 | def getBoundary(self): 80 | ''' 81 | this function returns the string used in the mime message as a 82 | boundary. First the write method as to be called 83 | ''' 84 | return self._boundary 85 | 86 | def getStartCID(self): 87 | ''' 88 | This function returns the CID of the XML message 89 | ''' 90 | return self._startCID 91 | 92 | 93 | def _make_boundary(text=None): 94 | #some code taken from python stdlib 95 | # Craft a random boundary. If text is given, ensure that the chosen 96 | # boundary doesn't appear in the text. 97 | token = random.randrange(sys.maxint) 98 | boundary = ('=' * 10) + (_fmt % token) + '==' 99 | if text is None: 100 | return boundary 101 | b = boundary 102 | counter = 0 103 | while True: 104 | cre = re.compile('^--' + re.escape(b) + '(--)?$', re.MULTILINE) 105 | if not cre.search(text): 106 | break 107 | b = boundary + '.' + str(counter) 108 | counter += 1 109 | return b 110 | 111 | -------------------------------------------------------------------------------- /wstools/TimeoutSocket.py: -------------------------------------------------------------------------------- 1 | """Based on code from timeout_socket.py, with some tweaks for compatibility. 2 | These tweaks should really be rolled back into timeout_socket, but it's 3 | not totally clear who is maintaining it at this point. In the meantime, 4 | we'll use a different module name for our tweaked version to avoid any 5 | confusion. 6 | 7 | The original timeout_socket is by: 8 | 9 | Scott Cotton 10 | Lloyd Zusman 11 | Phil Mayes 12 | Piers Lauder 13 | Radovan Garabik 14 | """ 15 | 16 | ident = "$Id$" 17 | 18 | import string, socket, select, errno 19 | 20 | WSAEINVAL = getattr(errno, 'WSAEINVAL', 10022) 21 | 22 | 23 | class TimeoutSocket: 24 | """A socket imposter that supports timeout limits.""" 25 | 26 | def __init__(self, timeout=20, sock=None): 27 | self.timeout = float(timeout) 28 | self.inbuf = '' 29 | if sock is None: 30 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 31 | self.sock = sock 32 | self.sock.setblocking(0) 33 | self._rbuf = '' 34 | self._wbuf = '' 35 | 36 | def __getattr__(self, name): 37 | # Delegate to real socket attributes. 38 | return getattr(self.sock, name) 39 | 40 | def connect(self, *addr): 41 | timeout = self.timeout 42 | sock = self.sock 43 | try: 44 | # Non-blocking mode 45 | sock.setblocking(0) 46 | apply(sock.connect, addr) 47 | sock.setblocking(timeout != 0) 48 | return 1 49 | except socket.error,why: 50 | if not timeout: 51 | raise 52 | sock.setblocking(1) 53 | if len(why.args) == 1: 54 | code = 0 55 | else: 56 | code, why = why 57 | if code not in ( 58 | errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK 59 | ): 60 | raise 61 | r,w,e = select.select([],[sock],[],timeout) 62 | if w: 63 | try: 64 | apply(sock.connect, addr) 65 | return 1 66 | except socket.error,why: 67 | if len(why.args) == 1: 68 | code = 0 69 | else: 70 | code, why = why 71 | if code in (errno.EISCONN, WSAEINVAL): 72 | return 1 73 | raise 74 | raise TimeoutError('socket connect() timeout.') 75 | 76 | def send(self, data, flags=0): 77 | total = len(data) 78 | next = 0 79 | while 1: 80 | r, w, e = select.select([],[self.sock], [], self.timeout) 81 | if w: 82 | buff = data[next:next + 8192] 83 | sent = self.sock.send(buff, flags) 84 | next = next + sent 85 | if next == total: 86 | return total 87 | continue 88 | raise TimeoutError('socket send() timeout.') 89 | 90 | def recv(self, amt, flags=0): 91 | if select.select([self.sock], [], [], self.timeout)[0]: 92 | return self.sock.recv(amt, flags) 93 | raise TimeoutError('socket recv() timeout.') 94 | 95 | buffsize = 4096 96 | handles = 1 97 | 98 | def makefile(self, mode="r", buffsize=-1): 99 | self.handles = self.handles + 1 100 | self.mode = mode 101 | return self 102 | 103 | def close(self): 104 | self.handles = self.handles - 1 105 | if self.handles == 0 and self.sock.fileno() >= 0: 106 | self.sock.close() 107 | 108 | def read(self, n=-1): 109 | if not isinstance(n, type(1)): 110 | n = -1 111 | if n >= 0: 112 | k = len(self._rbuf) 113 | if n <= k: 114 | data = self._rbuf[:n] 115 | self._rbuf = self._rbuf[n:] 116 | return data 117 | n = n - k 118 | L = [self._rbuf] 119 | self._rbuf = "" 120 | while n > 0: 121 | new = self.recv(max(n, self.buffsize)) 122 | if not new: break 123 | k = len(new) 124 | if k > n: 125 | L.append(new[:n]) 126 | self._rbuf = new[n:] 127 | break 128 | L.append(new) 129 | n = n - k 130 | return "".join(L) 131 | k = max(4096, self.buffsize) 132 | L = [self._rbuf] 133 | self._rbuf = "" 134 | while 1: 135 | new = self.recv(k) 136 | if not new: break 137 | L.append(new) 138 | k = min(k*2, 1024**2) 139 | return "".join(L) 140 | 141 | def readline(self, limit=-1): 142 | data = "" 143 | i = self._rbuf.find('\n') 144 | while i < 0 and not (0 < limit <= len(self._rbuf)): 145 | new = self.recv(self.buffsize) 146 | if not new: break 147 | i = new.find('\n') 148 | if i >= 0: i = i + len(self._rbuf) 149 | self._rbuf = self._rbuf + new 150 | if i < 0: i = len(self._rbuf) 151 | else: i = i+1 152 | if 0 <= limit < len(self._rbuf): i = limit 153 | data, self._rbuf = self._rbuf[:i], self._rbuf[i:] 154 | return data 155 | 156 | def readlines(self, sizehint = 0): 157 | total = 0 158 | list = [] 159 | while 1: 160 | line = self.readline() 161 | if not line: break 162 | list.append(line) 163 | total += len(line) 164 | if sizehint and total >= sizehint: 165 | break 166 | return list 167 | 168 | def writelines(self, list): 169 | self.send(''.join(list)) 170 | 171 | def write(self, data): 172 | self.send(data) 173 | 174 | def flush(self): 175 | pass 176 | 177 | 178 | class TimeoutError(Exception): 179 | pass 180 | -------------------------------------------------------------------------------- /wstools/UserTuple.py: -------------------------------------------------------------------------------- 1 | """ 2 | A more or less complete user-defined wrapper around tuple objects. 3 | Adapted version of the standard library's UserList. 4 | 5 | Taken from Stefan Schwarzer's ftputil library, available at 6 | , and used under this license: 7 | 8 | 9 | 10 | 11 | Copyright (C) 1999, Stefan Schwarzer 12 | All rights reserved. 13 | 14 | Redistribution and use in source and binary forms, with or without 15 | modification, are permitted provided that the following conditions are 16 | met: 17 | 18 | - Redistributions of source code must retain the above copyright 19 | notice, this list of conditions and the following disclaimer. 20 | 21 | - Redistributions in binary form must reproduce the above copyright 22 | notice, this list of conditions and the following disclaimer in the 23 | documentation and/or other materials provided with the distribution. 24 | 25 | - Neither the name of the above author nor the names of the 26 | contributors to the software may be used to endorse or promote 27 | products derived from this software without specific prior written 28 | permission. 29 | 30 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 31 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 32 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 33 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR 34 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 35 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 36 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 37 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 38 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 39 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 40 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41 | 42 | """ 43 | 44 | 45 | 46 | 47 | # $Id$ 48 | 49 | #XXX tuple instances (in Python 2.2) contain also: 50 | # __class__, __delattr__, __getattribute__, __hash__, __new__, 51 | # __reduce__, __setattr__, __str__ 52 | # What about these? 53 | 54 | class UserTuple: 55 | def __init__(self, inittuple=None): 56 | self.data = () 57 | if inittuple is not None: 58 | # XXX should this accept an arbitrary sequence? 59 | if type(inittuple) == type(self.data): 60 | self.data = inittuple 61 | elif isinstance(inittuple, UserTuple): 62 | # this results in 63 | # self.data is inittuple.data 64 | # but that's ok for tuples because they are 65 | # immutable. (Builtin tuples behave the same.) 66 | self.data = inittuple.data[:] 67 | else: 68 | # the same applies here; (t is tuple(t)) == 1 69 | self.data = tuple(inittuple) 70 | def __repr__(self): return repr(self.data) 71 | def __lt__(self, other): return self.data < self.__cast(other) 72 | def __le__(self, other): return self.data <= self.__cast(other) 73 | def __eq__(self, other): return self.data == self.__cast(other) 74 | def __ne__(self, other): return self.data != self.__cast(other) 75 | def __gt__(self, other): return self.data > self.__cast(other) 76 | def __ge__(self, other): return self.data >= self.__cast(other) 77 | def __cast(self, other): 78 | if isinstance(other, UserTuple): return other.data 79 | else: return other 80 | def __cmp__(self, other): 81 | return cmp(self.data, self.__cast(other)) 82 | def __contains__(self, item): return item in self.data 83 | def __len__(self): return len(self.data) 84 | def __getitem__(self, i): return self.data[i] 85 | def __getslice__(self, i, j): 86 | i = max(i, 0); j = max(j, 0) 87 | return self.__class__(self.data[i:j]) 88 | def __add__(self, other): 89 | if isinstance(other, UserTuple): 90 | return self.__class__(self.data + other.data) 91 | elif isinstance(other, type(self.data)): 92 | return self.__class__(self.data + other) 93 | else: 94 | return self.__class__(self.data + tuple(other)) 95 | # dir( () ) contains no __radd__ (at least in Python 2.2) 96 | def __mul__(self, n): 97 | return self.__class__(self.data*n) 98 | __rmul__ = __mul__ 99 | 100 | -------------------------------------------------------------------------------- /wstools/XMLname.py: -------------------------------------------------------------------------------- 1 | """Translate strings to and from SOAP 1.2 XML name encoding 2 | 3 | Implements rules for mapping application defined name to XML names 4 | specified by the w3 SOAP working group for SOAP version 1.2 in 5 | Appendix A of "SOAP Version 1.2 Part 2: Adjuncts", W3C Working Draft 6 | 17, December 2001, 7 | 8 | Also see . 9 | 10 | Author: Gregory R. Warnes 11 | Date:: 2002-04-25 12 | Version 0.9.0 13 | 14 | """ 15 | 16 | ident = "$Id$" 17 | 18 | from re import * 19 | 20 | 21 | def _NCNameChar(x): 22 | return x.isalpha() or x.isdigit() or x=="." or x=='-' or x=="_" 23 | 24 | 25 | def _NCNameStartChar(x): 26 | return x.isalpha() or x=="_" 27 | 28 | 29 | def _toUnicodeHex(x): 30 | hexval = hex(ord(x[0]))[2:] 31 | hexlen = len(hexval) 32 | # Make hexval have either 4 or 8 digits by prepending 0's 33 | if (hexlen==1): hexval = "000" + hexval 34 | elif (hexlen==2): hexval = "00" + hexval 35 | elif (hexlen==3): hexval = "0" + hexval 36 | elif (hexlen==4): hexval = "" + hexval 37 | elif (hexlen==5): hexval = "000" + hexval 38 | elif (hexlen==6): hexval = "00" + hexval 39 | elif (hexlen==7): hexval = "0" + hexval 40 | elif (hexlen==8): hexval = "" + hexval 41 | else: raise Exception, "Illegal Value returned from hex(ord(x))" 42 | 43 | return "_x"+ hexval + "_" 44 | 45 | 46 | def _fromUnicodeHex(x): 47 | return eval( r'u"\u'+x[2:-1]+'"' ) 48 | 49 | 50 | def toXMLname(string): 51 | """Convert string to a XML name.""" 52 | if string.find(':') != -1 : 53 | (prefix, localname) = string.split(':',1) 54 | else: 55 | prefix = None 56 | localname = string 57 | 58 | T = unicode(localname) 59 | 60 | N = len(localname) 61 | X = []; 62 | for i in range(N) : 63 | if i< N-1 and T[i]==u'_' and T[i+1]==u'x': 64 | X.append(u'_x005F_') 65 | elif i==0 and N >= 3 and \ 66 | ( T[0]==u'x' or T[0]==u'X' ) and \ 67 | ( T[1]==u'm' or T[1]==u'M' ) and \ 68 | ( T[2]==u'l' or T[2]==u'L' ): 69 | X.append(u'_xFFFF_' + T[0]) 70 | elif (not _NCNameChar(T[i])) or (i==0 and not _NCNameStartChar(T[i])): 71 | X.append(_toUnicodeHex(T[i])) 72 | else: 73 | X.append(T[i]) 74 | 75 | if prefix: 76 | return "%s:%s" % (prefix, u''.join(X)) 77 | return u''.join(X) 78 | 79 | 80 | def fromXMLname(string): 81 | """Convert XML name to unicode string.""" 82 | 83 | retval = sub(r'_xFFFF_','', string ) 84 | 85 | def fun( matchobj ): 86 | return _fromUnicodeHex( matchobj.group(0) ) 87 | 88 | retval = sub(r'_x[0-9A-Za-z]+_', fun, retval ) 89 | 90 | return retval 91 | -------------------------------------------------------------------------------- /wstools/__init__.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """WSDL parsing services package for Web Services for Python.""" 3 | 4 | ident = "$Id$" 5 | 6 | import WSDLTools 7 | import XMLname 8 | import logging 9 | 10 | -------------------------------------------------------------------------------- /wstools/tests/.cvsignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /wstools/tests/README: -------------------------------------------------------------------------------- 1 | Two top level modules have been provided to run the tests. "test_wstools.py" 2 | is used to run all of the local tests. "test_wstools_net.py" is used to run 3 | all of the tests that require network access. 4 | 5 | Add the -v option for more informative feedback. 6 | 7 | ADDING TESTS: 8 | 1. For Stand-Alone tests add WSDL FILE to appropriate archive file 9 | Need to add a NEW Archive?: 10 | config.txt [files] "archive" -- tuple of all archive files, 11 | if you need to create a new archive append the archive 12 | name to the 'archive' tuple. 13 | 14 | 2. Edit config.txt section(s): 15 | option -- name by which service will be referenced in test case. 16 | Need an entry under appropriate section(s), this name 17 | must be unique within each section it appears but it may 18 | appear in multiple sections. 19 | 20 | config.txt "test" sections: 21 | Stand-Alone -- add "option" under [services_by_file] 22 | eg. amazon = exports/AmazonWebServices.wsdl 23 | 24 | Network -- add "option" under [services_by_http] 25 | eg. amazon = http://soap.amazon.com/schemas/AmazonWebServices.wsdl 26 | 27 | Broken -- add "option" under [broken] 28 | 29 | 3. Done 30 | 31 | 32 | CONTENTS OF SAMPLE WSDL/XSD: 33 | schema -- Taken from globus-3.0.1(http://www.globus.org) 34 | xmethods -- Taken from XMethods(http://www.xmethods.com) 35 | airport.wsdl 36 | AmazonWebServices.wsdl 37 | books.wsdl 38 | Distance.wsdl 39 | freedb.wsdl 40 | globalweather.wsdl 41 | IHaddock.wsdl 42 | ip2geo.wsdl 43 | magic.wsdl 44 | query.wsdl 45 | RateInfo.wsdl 46 | SHA1Encrypt.wsdl 47 | siteInspect.wsdl 48 | TemperatureService.wsdl 49 | usweather.wsdl 50 | rtf2html.xml 51 | SolveSystem.wsdl.xml 52 | zip2geo.wsdl 53 | -------------------------------------------------------------------------------- /wstools/tests/__init__.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | -------------------------------------------------------------------------------- /wstools/tests/schema.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtoomim/p2pool/ece15b03fc2367d02e730b2b85b666217fa8985e/wstools/tests/schema.tar.gz -------------------------------------------------------------------------------- /wstools/tests/test_t1.py: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # Joshua R. Boverhof, David W. Robertson, LBNL 3 | # See LBNLCopyright for copyright notice! 4 | ########################################################################### 5 | import unittest 6 | import test_wsdl 7 | import utils 8 | 9 | def makeTestSuite(): 10 | suite = unittest.TestSuite() 11 | suite.addTest(test_wsdl.makeTestSuite("services_by_file")) 12 | return suite 13 | 14 | def main(): 15 | loader = utils.MatchTestLoader(True, None, "makeTestSuite") 16 | unittest.main(defaultTest="makeTestSuite", testLoader=loader) 17 | 18 | if __name__ == "__main__" : main() 19 | 20 | 21 | -------------------------------------------------------------------------------- /wstools/tests/test_wsdl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ############################################################################ 4 | # Joshua R. Boverhof, David W. Robertson, LBNL 5 | # See LBNLCopyright for copyright notice! 6 | ########################################################################### 7 | 8 | import sys, unittest 9 | import ConfigParser 10 | import os 11 | from wstools.Utility import DOM 12 | from wstools.WSDLTools import WSDLReader 13 | from wstools.TimeoutSocket import TimeoutError 14 | 15 | from wstools import tests 16 | cwd = os.path.dirname(tests.__file__) 17 | 18 | class WSDLToolsTestCase(unittest.TestCase): 19 | 20 | def __init__(self, methodName='runTest'): 21 | unittest.TestCase.__init__(self, methodName) 22 | 23 | def setUp(self): 24 | self.path = nameGenerator.next() 25 | print self.path 26 | sys.stdout.flush() 27 | 28 | def __str__(self): 29 | teststr = unittest.TestCase.__str__(self) 30 | if hasattr(self, "path"): 31 | return "%s: %s" % (teststr, self.path ) 32 | else: 33 | return "%s" % (teststr) 34 | 35 | def checkWSDLCollection(self, tag_name, component, key='name'): 36 | if self.wsdl is None: 37 | return 38 | definition = self.wsdl.document.documentElement 39 | version = DOM.WSDLUriToVersion(definition.namespaceURI) 40 | nspname = DOM.GetWSDLUri(version) 41 | for node in DOM.getElements(definition, tag_name, nspname): 42 | name = DOM.getAttr(node, key) 43 | comp = component[name] 44 | self.failUnlessEqual(eval('comp.%s' %key), name) 45 | 46 | def checkXSDCollection(self, tag_name, component, node, key='name'): 47 | for cnode in DOM.getElements(node, tag_name): 48 | name = DOM.getAttr(cnode, key) 49 | component[name] 50 | 51 | def test_all(self): 52 | try: 53 | if self.path[:7] == 'http://': 54 | self.wsdl = WSDLReader().loadFromURL(self.path) 55 | else: 56 | self.wsdl = WSDLReader().loadFromFile(self.path) 57 | 58 | except TimeoutError: 59 | print "connection timed out" 60 | sys.stdout.flush() 61 | return 62 | except: 63 | self.path = self.path + ": load failed, unable to start" 64 | raise 65 | 66 | try: 67 | self.checkWSDLCollection('service', self.wsdl.services) 68 | except: 69 | self.path = self.path + ": wsdl.services" 70 | raise 71 | 72 | try: 73 | self.checkWSDLCollection('message', self.wsdl.messages) 74 | except: 75 | self.path = self.path + ": wsdl.messages" 76 | raise 77 | 78 | try: 79 | self.checkWSDLCollection('portType', self.wsdl.portTypes) 80 | except: 81 | self.path = self.path + ": wsdl.portTypes" 82 | raise 83 | 84 | try: 85 | self.checkWSDLCollection('binding', self.wsdl.bindings) 86 | except: 87 | self.path = self.path + ": wsdl.bindings" 88 | raise 89 | 90 | try: 91 | self.checkWSDLCollection('import', self.wsdl.imports, key='namespace') 92 | except: 93 | self.path = self.path + ": wsdl.imports" 94 | raise 95 | 96 | try: 97 | for key in self.wsdl.types.keys(): 98 | schema = self.wsdl.types[key] 99 | self.failUnlessEqual(key, schema.getTargetNamespace()) 100 | 101 | definition = self.wsdl.document.documentElement 102 | version = DOM.WSDLUriToVersion(definition.namespaceURI) 103 | nspname = DOM.GetWSDLUri(version) 104 | for node in DOM.getElements(definition, 'types', nspname): 105 | for snode in DOM.getElements(node, 'schema'): 106 | tns = DOM.findTargetNS(snode) 107 | schema = self.wsdl.types[tns] 108 | self.schemaAttributesDeclarations(schema, snode) 109 | self.schemaAttributeGroupDeclarations(schema, snode) 110 | self.schemaElementDeclarations(schema, snode) 111 | self.schemaTypeDefinitions(schema, snode) 112 | except: 113 | self.path = self.path + ": wsdl.types" 114 | raise 115 | 116 | if self.wsdl.extensions: 117 | print 'No check for WSDLTools(%s) Extensions:' %(self.wsdl.name) 118 | for ext in self.wsdl.extensions: print '\t', ext 119 | 120 | def schemaAttributesDeclarations(self, schema, node): 121 | self.checkXSDCollection('attribute', schema.attr_decl, node) 122 | 123 | def schemaAttributeGroupDeclarations(self, schema, node): 124 | self.checkXSDCollection('group', schema.attr_groups, node) 125 | 126 | def schemaElementDeclarations(self, schema, node): 127 | self.checkXSDCollection('element', schema.elements, node) 128 | 129 | def schemaTypeDefinitions(self, schema, node): 130 | self.checkXSDCollection('complexType', schema.types, node) 131 | self.checkXSDCollection('simpleType', schema.types, node) 132 | 133 | 134 | def setUpOptions(section): 135 | cp = ConfigParser.ConfigParser() 136 | cp.read(cwd+'/config.txt') 137 | if not cp.sections(): 138 | print 'fatal error: configuration file config.txt not present' 139 | sys.exit(0) 140 | if not cp.has_section(section): 141 | print '%s section not present in configuration file, exiting' % section 142 | sys.exit(0) 143 | return cp, len(cp.options(section)) 144 | 145 | def getOption(cp, section): 146 | for name, value in cp.items(section): 147 | yield value 148 | 149 | def makeTestSuite(section='services_by_file'): 150 | global nameGenerator 151 | 152 | cp, numTests = setUpOptions(section) 153 | nameGenerator = getOption(cp, section) 154 | suite = unittest.TestSuite() 155 | for i in range(0, numTests): 156 | suite.addTest(unittest.makeSuite(WSDLToolsTestCase, 'test_')) 157 | return suite 158 | 159 | 160 | def main(): 161 | unittest.main(defaultTest="makeTestSuite") 162 | 163 | 164 | if __name__ == "__main__" : main() 165 | -------------------------------------------------------------------------------- /wstools/tests/test_wstools.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ############################################################################ 4 | # Joshua R. Boverhof, David W. Robertson, LBNL 5 | # See LBNLCopyright for copyright notice! 6 | ########################################################################### 7 | 8 | import unittest, tarfile, os, ConfigParser 9 | import test_wsdl 10 | 11 | 12 | SECTION='files' 13 | CONFIG_FILE = 'config.txt' 14 | 15 | def extractFiles(section, option): 16 | config = ConfigParser.ConfigParser() 17 | config.read(CONFIG_FILE) 18 | archives = config.get(section, option) 19 | archives = eval(archives) 20 | for file in archives: 21 | tar = tarfile.open(file) 22 | if not os.access(tar.membernames[0], os.R_OK): 23 | for i in tar.getnames(): 24 | tar.extract(i) 25 | 26 | def makeTestSuite(): 27 | suite = unittest.TestSuite() 28 | suite.addTest(test_wsdl.makeTestSuite("services_by_file")) 29 | return suite 30 | 31 | def main(): 32 | extractFiles(SECTION, 'archives') 33 | unittest.main(defaultTest="makeTestSuite") 34 | 35 | if __name__ == "__main__" : main() 36 | 37 | 38 | -------------------------------------------------------------------------------- /wstools/tests/test_wstools_net.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ############################################################################ 4 | # Joshua R. Boverhof, David W. Robertson, LBNL 5 | # See LBNLCopyright for copyright notice! 6 | ########################################################################### 7 | import unittest 8 | import test_wsdl 9 | 10 | def makeTestSuite(): 11 | suite = unittest.TestSuite() 12 | suite.addTest(test_wsdl.makeTestSuite("services_by_http")) 13 | return suite 14 | 15 | def main(): 16 | unittest.main(defaultTest="makeTestSuite") 17 | 18 | if __name__ == "__main__" : main() 19 | 20 | 21 | -------------------------------------------------------------------------------- /wstools/tests/xmethods.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtoomim/p2pool/ece15b03fc2367d02e730b2b85b666217fa8985e/wstools/tests/xmethods.tar.gz --------------------------------------------------------------------------------