NVC/PPC protocol changes support
authoralex <alex@alex-VirtualBox.(none)>
Thu, 7 Mar 2013 15:39:44 +0000 (19:39 +0400)
committeralex <alex@alex-VirtualBox.(none)>
Thu, 7 Mar 2013 15:39:44 +0000 (19:39 +0400)
13 files changed:
README
p2pool/bitcoin/data.py
p2pool/bitcoin/helper.py
p2pool/bitcoin/networks.py
p2pool/bitcoin/p2p.py
p2pool/data.py
p2pool/main.py
p2pool/networks.py
p2pool/node.py
p2pool/test/test_node.py
p2pool/util/jsonrpc.py
p2pool/web.py
p2pool/work.py

diff --git a/README b/README
index 1262a83..ab9b2a7 100644 (file)
--- a/README
+++ b/README
@@ -1,6 +1,7 @@
 Requirements:
     Generic:
-        Bitcoin >=0.6.4
+        Novacoin >= 0.3.1
+        ltc_scrypt
         Python
         Twisted
         python-argparse (for Python <=2.6)
@@ -15,27 +16,9 @@ Requirements:
         Install Zope.Interface: http://pypi.python.org/pypi/zope.interface/3.8.0
             Unzip the files into C:\Python27\Lib\site-packages
 
-Running P2Pool:
-    To use P2Pool, you must be running your own local bitcoind. For standard
-    configurations, using P2Pool should be as simple as:
-
-        python run_p2pool.py
-
-    Then run your miner program, connecting to 127.0.0.1 on port 9332 with any
-    username and password.
-
-    If you are behind a NAT, you should enable TCP port forwarding on your
-    router. Forward port 9333 to the host running P2Pool.
-
-    Run "python run_p2pool.py --help" for additional options.
-
-Donations towards further development:
-    1HNeqi3pJRNvXybNX4FKzZgYJsdTSqJTbk
-
-Notes for Litecoin:
-    Requirements:
-        In order to run P2Pool with the Litecoin network, you would need to build and install the
-        ltc_scrypt module that includes the scrypt proof of work code that Litecoin uses for hashes.
+Additional info about scrypt module:
+        In order to run P2Pool with the NovaCoin network, you would need to build and install the
+        ltc_scrypt module that includes the scrypt proof of work code that NovaCoin uses for block hashes.
 
         Linux:
             cd litecoin_scrypt
@@ -50,14 +33,20 @@ Notes for Litecoin:
 
             If you run into an error with unrecognized command line option '-mno-cygwin', see this:
                 http://stackoverflow.com/questions/6034390/compiling-with-cython-and-mingw-produces-gcc-error-unrecognized-command-line-o
-    
-    Running P2Pool:
-        Run P2Pool with the "--net litecoin" option.
-        Run your miner program, connecting to 127.0.0.1 on port 9327.
-        Forward port 9338 to the host running P2Pool.
-        
-        Litecoin's use of ports 9332 and 9332 conflicts with P2Pool running on
-        the Bitcoin network. To avoid problems, add these lines to litecoin.conf
-        and restart litecoind:
-            rpcport=10332
-            port=10333
+
+Running P2Pool:
+    To use P2Pool, you must be running your own local bitcoind. For standard
+    configurations, using P2Pool should be as simple as:
+
+        python run_p2pool.py --net novacoin
+
+    Then run your miner program, connecting to 127.0.0.1 on port 8336 with any
+    username and password.
+
+    If you are behind a NAT, you should enable TCP port forwarding on your
+    router. Forward port 9777 to the host running P2Pool.
+
+    Run "python run_p2pool.py --help" for additional options.
+
+Donations towards further development:
+    1HNeqi3pJRNvXybNX4FKzZgYJsdTSqJTbk
index de6d405..0ce113b 100644 (file)
@@ -92,6 +92,7 @@ address_type = pack.ComposedType([
 
 tx_type = pack.ComposedType([
     ('version', pack.IntType(32)),
+    ('timestamp', pack.IntType(32)),
     ('tx_ins', pack.ListType(pack.ComposedType([
         ('previous_output', pack.PossiblyNoneType(dict(hash=0, index=2**32 - 1), pack.ComposedType([
             ('hash', pack.IntType(256)),
@@ -130,6 +131,7 @@ block_header_type = pack.ComposedType([
 block_type = pack.ComposedType([
     ('header', block_header_type),
     ('txs', pack.ListType(tx_type)),
+    ('signature', pack.VarStrType()),
 ])
 
 # merged mining
index a46f5fd..d4fb288 100644 (file)
@@ -62,17 +62,20 @@ def getwork(bitcoind, use_getblocktemplate=False):
 @deferral.retry('Error submitting primary block: (will retry)', 10, 10)
 def submit_block_p2p(block, factory, net):
     if factory.conn.value is None:
-        print >>sys.stderr, 'No bitcoind connection when block submittal attempted! %s%064x' % (net.PARENT.BLOCK_EXPLORER_URL_PREFIX, bitcoin_data.hash256(bitcoin_data.block_header_type.pack(block['header'])))
+        print >>sys.stderr, 'No bitcoind connection when block submittal attempted! %s%064x' % (net.PARENT.BLOCK_EXPLORER_URL_PREFIX, self.node.net.PARENT.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(block['header'])))
         raise deferral.RetrySilentlyException()
     factory.conn.value.send_block(block=block)
 
 @deferral.retry('Error submitting block: (will retry)', 10, 10)
 @defer.inlineCallbacks
 def submit_block_rpc(block, ignore_failure, bitcoind, bitcoind_work, net):
+
     if bitcoind_work.value['use_getblocktemplate']:
         result = yield bitcoind.rpc_submitblock(bitcoin_data.block_type.pack(block).encode('hex'))
         success = result is None
     else:
+        print bitcoin_data.block_type.pack(block).encode('hex')
+
         result = yield bitcoind.rpc_getmemorypool(bitcoin_data.block_type.pack(block).encode('hex'))
         success = result
     success_expected = net.PARENT.POW_FUNC(bitcoin_data.block_header_type.pack(block['header'])) <= block['header']['bits'].target
@@ -80,5 +83,5 @@ def submit_block_rpc(block, ignore_failure, bitcoind, bitcoind_work, net):
         print >>sys.stderr, 'Block submittal result: %s (%r) Expected: %s' % (success, result, success_expected)
 
 def submit_block(block, ignore_failure, factory, bitcoind, bitcoind_work, net):
-    submit_block_p2p(block, factory, net)
+    #submit_block_p2p(block, factory, net)
     submit_block_rpc(block, ignore_failure, bitcoind, bitcoind_work, net)
index ea53f05..8831470 100644 (file)
@@ -5,156 +5,66 @@ from twisted.internet import defer
 
 from . import data
 from p2pool.util import math, pack
+from operator import *
+
+def get_subsidy(nCap, nMaxSubsidy, bnTarget):
+    bnLowerBound = 0.01
+    bnUpperBound = bnSubsidyLimit = nMaxSubsidy
+    bnTargetLimit = 0x00000fffff000000000000000000000000000000000000000000000000000000
+
+    while bnLowerBound + 0.01 <= bnUpperBound:
+        bnMidValue = (bnLowerBound + bnUpperBound) / 2
+        if pow(bnMidValue, nCap) * bnTargetLimit > pow(bnSubsidyLimit, nCap) * bnTarget:
+            bnUpperBound = bnMidValue
+        else:
+            bnLowerBound = bnMidValue
+
+    nSubsidy = round(bnMidValue, 2)
+
+    if nSubsidy > bnMidValue:
+        nSubsidy = nSubsidy - 0.01
+
+    return int(nSubsidy * 1000000)
 
 nets = dict(
-    bitcoin=math.Object(
-        P2P_PREFIX='f9beb4d9'.decode('hex'),
-        P2P_PORT=8333,
-        ADDRESS_VERSION=0,
-        RPC_PORT=8332,
+    novacoin=math.Object(
+        P2P_PREFIX='e4e8e9e5'.decode('hex'),
+        P2P_PORT=7777,
+        ADDRESS_VERSION=8,
+        RPC_PORT=8344,
         RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue(
-            'bitcoinaddress' in (yield bitcoind.rpc_help()) and
+            'novacoinaddress' in (yield bitcoind.rpc_help()) and
             not (yield bitcoind.rpc_getinfo())['testnet']
         )),
-        SUBSIDY_FUNC=lambda height: 50*100000000 >> (height + 1)//210000,
-        POW_FUNC=data.hash256,
-        BLOCK_PERIOD=600, # s
-        SYMBOL='BTC',
-        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'),
-        BLOCK_EXPLORER_URL_PREFIX='http://blockexplorer.com/block/',
-        ADDRESS_EXPLORER_URL_PREFIX='http://blockexplorer.com/address/',
-        SANE_TARGET_RANGE=(2**256//2**32//1000 - 1, 2**256//2**32 - 1),
-    ),
-    bitcoin_testnet=math.Object(
-        P2P_PREFIX='0b110907'.decode('hex'),
-        P2P_PORT=18333,
-        ADDRESS_VERSION=111,
-        RPC_PORT=18332,
-        RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue(
-            'bitcoinaddress' in (yield bitcoind.rpc_help()) and
-            (yield bitcoind.rpc_getinfo())['testnet']
-        )),
-        SUBSIDY_FUNC=lambda height: 50*100000000 >> (height + 1)//210000,
-        POW_FUNC=data.hash256,
-        BLOCK_PERIOD=600, # s
-        SYMBOL='tBTC',
-        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'),
-        BLOCK_EXPLORER_URL_PREFIX='http://blockexplorer.com/testnet/block/',
-        ADDRESS_EXPLORER_URL_PREFIX='http://blockexplorer.com/testnet/address/',
-        SANE_TARGET_RANGE=(2**256//2**32//1000 - 1, 2**256//2**32 - 1),
-    ),
-    
-    namecoin=math.Object(
-        P2P_PREFIX='f9beb4fe'.decode('hex'),
-        P2P_PORT=8334,
-        ADDRESS_VERSION=52,
-        RPC_PORT=8332,
-        RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue(
-            'namecoinaddress' in (yield bitcoind.rpc_help()) and
-            not (yield bitcoind.rpc_getinfo())['testnet']
-        )),
-        SUBSIDY_FUNC=lambda height: 50*100000000 >> (height + 1)//210000,
-        POW_FUNC=data.hash256,
-        BLOCK_PERIOD=600, # s
-        SYMBOL='NMC',
-        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'), 'bitcoin.conf'),
-        BLOCK_EXPLORER_URL_PREFIX='http://explorer.dot-bit.org/b/',
-        ADDRESS_EXPLORER_URL_PREFIX='http://explorer.dot-bit.org/a/',
-        SANE_TARGET_RANGE=(2**256//2**32 - 1, 2**256//2**32 - 1),
-    ),
-    namecoin_testnet=math.Object(
-        P2P_PREFIX='fabfb5fe'.decode('hex'),
-        P2P_PORT=18334,
-        ADDRESS_VERSION=111,
-        RPC_PORT=8332,
-        RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue(
-            'namecoinaddress' in (yield bitcoind.rpc_help()) and
-            (yield bitcoind.rpc_getinfo())['testnet']
-        )),
-        SUBSIDY_FUNC=lambda height: 50*100000000 >> (height + 1)//210000,
-        POW_FUNC=data.hash256,
-        BLOCK_PERIOD=600, # s
-        SYMBOL='tNMC',
-        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'), 'bitcoin.conf'),
-        BLOCK_EXPLORER_URL_PREFIX='http://testnet.explorer.dot-bit.org/b/',
-        ADDRESS_EXPLORER_URL_PREFIX='http://testnet.explorer.dot-bit.org/a/',
-        SANE_TARGET_RANGE=(2**256//2**32 - 1, 2**256//2**32 - 1),
-    ),
-    
-    litecoin=math.Object(
-        P2P_PREFIX='fbc0b6db'.decode('hex'),
-        P2P_PORT=9333,
-        ADDRESS_VERSION=48,
-        RPC_PORT=9332,
-        RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue(
-            'litecoinaddress' in (yield bitcoind.rpc_help()) and
-            not (yield bitcoind.rpc_getinfo())['testnet']
-        )),
-        SUBSIDY_FUNC=lambda height: 50*100000000 >> (height + 1)//840000,
+        SUBSIDY_FUNC=lambda target: get_subsidy(6, 100, target),
+        BLOCKHASH_FUNC=lambda data: pack.IntType(256).unpack(__import__('ltc_scrypt').getPoWHash(data)),
         POW_FUNC=lambda data: pack.IntType(256).unpack(__import__('ltc_scrypt').getPoWHash(data)),
-        BLOCK_PERIOD=150, # s
-        SYMBOL='LTC',
-        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'),
-        BLOCK_EXPLORER_URL_PREFIX='http://explorer.litecoin.net/block/',
-        ADDRESS_EXPLORER_URL_PREFIX='http://explorer.litecoin.net/address/',
+        BLOCK_PERIOD=600, # s
+        SYMBOL='NVC',
+        CONF_FILE_FUNC=lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'NovaCoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/NovaCoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.novacoin'), 'novacoin.conf'),
+        BLOCK_EXPLORER_URL_PREFIX='http://novacoin.ru/block/',
+        ADDRESS_EXPLORER_URL_PREFIX='http://novacoin.ru/address/',
         SANE_TARGET_RANGE=(2**256//1000000000 - 1, 2**256//1000 - 1),
     ),
-    litecoin_testnet=math.Object(
-        P2P_PREFIX='fcc1b7dc'.decode('hex'),
-        P2P_PORT=19333,
+    novacoin_testnet=math.Object(
+        P2P_PREFIX='cdf2c0ef'.decode('hex'),
+        P2P_PORT=17777,
         ADDRESS_VERSION=111,
-        RPC_PORT=19332,
+        RPC_PORT=8344,
         RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue(
-            'litecoinaddress' in (yield bitcoind.rpc_help()) and
+            'novacoinaddress' in (yield bitcoind.rpc_help()) and
             (yield bitcoind.rpc_getinfo())['testnet']
         )),
-        SUBSIDY_FUNC=lambda height: 50*100000000 >> (height + 1)//840000,
+        SUBSIDY_FUNC=lambda target: get_subsidy(6, 100, target),
+        BLOCKHASH_FUNC=lambda data: pack.IntType(256).unpack(__import__('ltc_scrypt').getPoWHash(data)),
         POW_FUNC=lambda data: pack.IntType(256).unpack(__import__('ltc_scrypt').getPoWHash(data)),
-        BLOCK_PERIOD=150, # s
-        SYMBOL='tLTC',
-        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'),
-        BLOCK_EXPLORER_URL_PREFIX='http://nonexistent-litecoin-testnet-explorer/block/',
-        ADDRESS_EXPLORER_URL_PREFIX='http://nonexistent-litecoin-testnet-explorer/address/',
-        SANE_TARGET_RANGE=(2**256//1000000000 - 1, 2**256 - 1),
-    ),
-
-    terracoin=math.Object(
-        P2P_PREFIX='42babe56'.decode('hex'),
-        P2P_PORT=13333,
-        ADDRESS_VERSION=0,
-        RPC_PORT=13332,
-        RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue(
-            'terracoinaddress' in (yield bitcoind.rpc_help()) and
-            not (yield bitcoind.rpc_getinfo())['testnet']
-        )),
-        SUBSIDY_FUNC=lambda height: 20*100000000 >> (height + 1)//1050000,
-        POW_FUNC=data.hash256,
-        BLOCK_PERIOD=120, # s
-        SYMBOL='TRC',
-        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'),
-        BLOCK_EXPLORER_URL_PREFIX='http://cryptocoinexplorer.com:3750/block/',
-        ADDRESS_EXPLORER_URL_PREFIX='http://cryptocoinexplorer.com:3750/address/',
-        SANE_TARGET_RANGE=(2**256//2**32//1000 - 1, 2**256//2**32 - 1),
-    ),
-    terracoin_testnet=math.Object(
-        P2P_PREFIX='41babe56'.decode('hex'),
-        P2P_PORT=23333,
-        ADDRESS_VERSION=111,
-        RPC_PORT=23332,
-        RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue(
-            'terracoinaddress' in (yield bitcoind.rpc_help()) and
-            (yield bitcoind.rpc_getinfo())['testnet']
-        )),
-        SUBSIDY_FUNC=lambda height: 20*100000000 >> (height + 1)//1050000,
-        POW_FUNC=data.hash256,
-        BLOCK_PERIOD=120, # s
-        SYMBOL='tTRC',
-        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'),
-        BLOCK_EXPLORER_URL_PREFIX='http://cryptocoinexplorer.com:3750/testnet/block/',
-        ADDRESS_EXPLORER_URL_PREFIX='http://cryptocoinexplorer.com:3750/testnet/address/',
-        SANE_TARGET_RANGE=(2**256//2**32//1000 - 1, 2**256//2**32 - 1),
+        BLOCK_PERIOD=600, # s
+        SYMBOL='tNVC',
+        CONF_FILE_FUNC=lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'NovaCoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/NovaCoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.novacoin'), 'novacoin.conf'),
+        BLOCK_EXPLORER_URL_PREFIX='http://nonexistent-novacoin-testnet-explorer/block/',
+        ADDRESS_EXPLORER_URL_PREFIX='http://nonexistent-novacoin-testnet-explorer/address/',
+        SANE_TARGET_RANGE=(2**256//1000000000 - 1, 2**256//1000 - 1),
     ),
-
 )
 for net_name, net in nets.iteritems():
     net.NAME = net_name
index fd403f1..6cec4b6 100644 (file)
@@ -15,7 +15,8 @@ from p2pool.util import deferral, p2protocol, pack, variable
 class Protocol(p2protocol.Protocol):
     def __init__(self, net):
         p2protocol.Protocol.__init__(self, net.P2P_PREFIX, 1000000)
-    
+        self.net = net
+
     def connectionMade(self):
         self.send_version(
             version=32200,
@@ -115,7 +116,7 @@ class Protocol(p2protocol.Protocol):
         ('block', bitcoin_data.block_type),
     ])
     def handle_block(self, block):
-        block_hash = bitcoin_data.hash256(bitcoin_data.block_header_type.pack(block['header']))
+        block_hash = self.net.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(block['header']))
         self.get_block.got_response(block_hash, block)
         self.get_block_header.got_response(block_hash, block['header'])
     
@@ -125,7 +126,7 @@ class Protocol(p2protocol.Protocol):
     def handle_headers(self, headers):
         for header in headers:
             header = header['header']
-            self.get_block_header.got_response(bitcoin_data.hash256(bitcoin_data.block_header_type.pack(header)), header)
+            self.get_block_header.got_response(self.net.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(header)), header)
         self.factory.new_headers.happened([header['header'] for header in headers])
     
     message_ping = pack.ComposedType([])
index 5ab1e7b..0456a28 100644 (file)
@@ -5,6 +5,7 @@ import os
 import random
 import sys
 import time
+import math
 
 from twisted.python import log
 
@@ -12,6 +13,8 @@ import p2pool
 from p2pool.bitcoin import data as bitcoin_data, script, sha256
 from p2pool.util import math, forest, pack
 
+minout = pow(10, 6) / 100;
+
 # hashlink
 
 hash_link_type = pack.ComposedType([
@@ -56,7 +59,7 @@ class Share(object):
     SUCCESSOR = None
     
     other_txs = None
-    
+
     small_block_header_type = pack.ComposedType([
         ('version', pack.VarIntType()),
         ('previous_block', pack.PossiblyNoneType(0, pack.IntType(256))),
@@ -70,7 +73,7 @@ class Share(object):
             ('previous_share_hash', pack.PossiblyNoneType(0, pack.IntType(256))),
             ('coinbase', pack.VarStrType()),
             ('nonce', pack.IntType(32)),
-            ('pubkey_hash', pack.IntType(160)),
+            ('pubkey', pack.FixedStrType(33)),
             ('subsidy', pack.IntType(64)),
             ('donation', pack.IntType(16)),
             ('stale_info', pack.EnumType(pack.IntType(8), dict((k, {0: None, 253: 'orphan', 254: 'doa'}.get(k, 'unk%i' % (k,))) for k in xrange(256)))),
@@ -103,13 +106,30 @@ class Share(object):
         ('identifier', pack.FixedStrType(64//8)),
         ('share_info', share_info_type),
     ])
-    
-    gentx_before_refhash = pack.VarStrType().pack(DONATION_SCRIPT) + pack.IntType(64).pack(0) + pack.VarStrType().pack('\x24' + pack.IntType(256).pack(0) + pack.IntType(32).pack(0))[:2]
-    
+
+
+    gentx_before_refhash = pack.VarStrType().pack(DONATION_SCRIPT) + pack.IntType(64).pack(minout) + pack.VarStrType().pack('\x24' + pack.IntType(256).pack(0) + pack.IntType(32).pack(0))[:2]
+
     @classmethod
     def generate_transaction(cls, tracker, share_data, block_target, desired_timestamp, desired_target, ref_merkle_link, desired_other_transaction_hashes_and_fees, net, known_txs=None, last_txout_nonce=0, base_subsidy=None):
         previous_share = tracker.items[share_data['previous_share_hash']] if share_data['previous_share_hash'] is not None else None
-        
+
+        def get_coinbase_fee(outpointsnum):
+            # calculate neccessary coinbase fee
+            coinbase_size = 59 + outpointsnum * 44 + 50
+
+            # if coinbase size is greater than 1000 bytes, it should pay fee (0.01 per 1000 bytes)
+            if coinbase_size > 1000:
+                return floor(coinbase_size / 1000.0) * minout
+
+            return 0
+
+        if base_subsidy is None:
+            base_subsidy = net.PARENT.SUBSIDY_FUNC(block_target)
+
+        # current user payout script
+        this_script = bitcoin_data.pubkey_to_script2(share_data['pubkey'])
+
         height, last = tracker.get_height_and_last(share_data['previous_share_hash'])
         assert height >= net.REAL_CHAIN_LENGTH or last is None
         if height < net.TARGET_LOOKBEHIND:
@@ -121,12 +141,12 @@ class Share(object):
             pre_target3 = math.clip(pre_target2, (net.MIN_TARGET, net.MAX_TARGET))
         max_bits = bitcoin_data.FloatingInteger.from_target_upper_bound(pre_target3)
         bits = bitcoin_data.FloatingInteger.from_target_upper_bound(math.clip(desired_target, (pre_target3//10, pre_target3)))
-        
+
         new_transaction_hashes = []
         new_transaction_size = 0
         transaction_hash_refs = []
         other_transaction_hashes = []
-        
+
         past_shares = list(tracker.get_chain(share_data['previous_share_hash'], min(height, 100)))
         tx_hash_to_this = {}
         for i, share in enumerate(past_shares):
@@ -146,32 +166,60 @@ class Share(object):
                 this = [0, len(new_transaction_hashes)-1]
             transaction_hash_refs.extend(this)
             other_transaction_hashes.append(tx_hash)
-        
+
         included_transactions = set(other_transaction_hashes)
-        removed_fees = [fee for tx_hash, fee in desired_other_transaction_hashes_and_fees if tx_hash not in included_transactions]
-        definite_fees = sum(0 if fee is None else fee for tx_hash, fee in desired_other_transaction_hashes_and_fees if tx_hash in included_transactions)
-        if None not in removed_fees:
-            share_data = dict(share_data, subsidy=share_data['subsidy'] - sum(removed_fees))
-        else:
-            assert base_subsidy is not None
-            share_data = dict(share_data, subsidy=base_subsidy + definite_fees)
-        
+
+        share_data = dict(share_data, subsidy=base_subsidy)
+
         weights, total_weight, donation_weight = tracker.get_cumulative_weights(share_data['previous_share_hash'],
             min(height, net.REAL_CHAIN_LENGTH),
             65535*net.SPREAD*bitcoin_data.target_to_average_attempts(block_target),
         )
+
+        # calculate "raw" subsidy
+        raw_subsidy = share_data['subsidy'] - 3 * minout - get_coinbase_fee(len(weights))
+
+        # calculate "raw" amounts
+        raw_amounts = dict((script, raw_subsidy*weight//total_weight) for script, weight in weights.iteritems()) 
+
+        total_remowed_weight = 0
+
+        # iterate list and collect all weights, which produces less than 0.01 payout
+        # it's neccessary due to NVC/PPC protocol-level limitations for coinbase outpoint size
+        for x in raw_amounts.keys():
+            if raw_amounts[x] < minout and x not in [this_script, DONATION_SCRIPT]:
+                total_remowed_weight = total_remowed_weight + weights[x]
+                del weights[x]
+
+        total_weight = total_weight - total_remowed_weight
         assert total_weight == sum(weights.itervalues()) + donation_weight, (total_weight, sum(weights.itervalues()) + donation_weight)
-        
-        amounts = dict((script, share_data['subsidy']*(199*weight)//(200*total_weight)) for script, weight in weights.iteritems()) # 99.5% goes according to weights prior to this share
-        this_script = bitcoin_data.pubkey_hash_to_script2(share_data['pubkey_hash'])
-        amounts[this_script] = amounts.get(this_script, 0) + share_data['subsidy']//200 # 0.5% goes to block finder
-        amounts[DONATION_SCRIPT] = amounts.get(DONATION_SCRIPT, 0) + share_data['subsidy'] - sum(amounts.itervalues()) # all that's left over is the donation weight and some extra satoshis due to rounding
-        
-        if sum(amounts.itervalues()) != share_data['subsidy'] or any(x < 0 for x in amounts.itervalues()):
+
+
+        # base subsidy value calculated as:
+        # [subsidy - (0.01 for donation + 0.01 for current user + 0.01 for p2pool outpoint) - netfee]
+        my_subsidy = share_data['subsidy'] - 3 * minout - get_coinbase_fee(len(weights) + 1)
+
+        # subsidy goes according to weights prior to this share
+        amounts = dict((script, my_subsidy*weight//total_weight) for script, weight in weights.iteritems()) 
+
+        # all that's left over is the donation weight and some extra satoshis due to rounding
+        amounts[DONATION_SCRIPT] = amounts.get(DONATION_SCRIPT, 0) + my_subsidy - sum(amounts.itervalues()) 
+
+        if sum(amounts.itervalues()) != my_subsidy or any(x < 0 for x in amounts.itervalues()):
             raise ValueError()
-        
+
+        # add 0.01 coin to donation, to satisfy the protocol
+        amounts[DONATION_SCRIPT] = amounts[DONATION_SCRIPT] + minout
+
+        # add 0.01 to current user output, to satisfy the protocol
+        amounts[this_script] = amounts.get(this_script, 0) + minout
+
+#        print amounts
+
         dests = sorted(amounts.iterkeys(), key=lambda script: (script == DONATION_SCRIPT, amounts[script], script))[-4000:] # block length limit, unlikely to ever be hit
-        
+
+#        print dests
+
         share_info = dict(
             share_data=share_data,
             far_share_hash=None if last is None and height < 99 else tracker.get_nth_parent_hash(share_data['previous_share_hash'], 99),
@@ -184,21 +232,27 @@ class Share(object):
             new_transaction_hashes=new_transaction_hashes,
             transaction_hash_refs=transaction_hash_refs,
         )
-        
+
         gentx = dict(
             version=1,
+            # coinbase timestamp must be older than share/block timestamp
+            # maybe there are more elegant solution, but this hack works quite well for now
+            timestamp=share_info['timestamp'],
             tx_ins=[dict(
                 previous_output=None,
                 sequence=None,
                 script=share_data['coinbase'],
             )],
             tx_outs=[dict(value=amounts[script], script=script) for script in dests if amounts[script] or script == DONATION_SCRIPT] + [dict(
-                value=0,
+                # add 0.01 coin to service output, to satisfy the protocol
+                value=minout,
                 script='\x24' + cls.get_ref_hash(net, share_info, ref_merkle_link) + pack.IntType(32).pack(last_txout_nonce),
             )],
             lock_time=0,
         )
-        
+
+        #print gentx
+
         def get_share(header, last_txout_nonce=last_txout_nonce):
             min_header = dict(header); del min_header['merkle_root']
             share = cls(net, None, dict(
@@ -246,7 +300,7 @@ class Share(object):
         self.target = self.share_info['bits'].target
         self.timestamp = self.share_info['timestamp']
         self.previous_hash = self.share_data['previous_share_hash']
-        self.new_script = bitcoin_data.pubkey_hash_to_script2(self.share_data['pubkey_hash'])
+        self.new_script = bitcoin_data.pubkey_to_script2(self.share_data['pubkey'])
         self.desired_version = self.share_data['desired_version']
         
         n = set()
@@ -264,7 +318,7 @@ class Share(object):
         merkle_root = bitcoin_data.check_merkle_link(self.gentx_hash, self.merkle_link)
         self.header = dict(self.min_header, merkle_root=merkle_root)
         self.pow_hash = net.PARENT.POW_FUNC(bitcoin_data.block_header_type.pack(self.header))
-        self.hash = self.header_hash = bitcoin_data.hash256(bitcoin_data.block_header_type.pack(self.header))
+        self.hash = self.header_hash = net.PARENT.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(self.header))
         
         if self.target > net.MAX_TARGET:
             from p2pool import p2p
@@ -273,7 +327,7 @@ class Share(object):
         if self.pow_hash > self.target:
             from p2pool import p2p
             raise p2p.PeerMisbehavingError('share PoW invalid')
-        
+
         self.new_transaction_hashes = self.share_info['new_transaction_hashes']
         
         # XXX eww
@@ -308,10 +362,24 @@ class Share(object):
                 raise p2p.PeerMisbehavingError('''%s can't follow %s''' % (type(self).__name__, type(previous_share).__name__))
         
         other_tx_hashes = [tracker.items[tracker.get_nth_parent_hash(self.hash, share_count)].share_info['new_transaction_hashes'][tx_count] for share_count, tx_count in self.iter_transaction_hash_refs()]
-        
-        share_info, gentx, other_tx_hashes2, get_share = self.generate_transaction(tracker, self.share_info['share_data'], self.header['bits'].target, self.share_info['timestamp'], self.share_info['bits'].target, self.contents['ref_merkle_link'], [(h, None) for h in other_tx_hashes], self.net, last_txout_nonce=self.contents['last_txout_nonce'])
+#        print self
+
+        share_info, gentx, other_tx_hashes2, get_share = self.generate_transaction(
+            tracker, 
+            self.share_info['share_data'], 
+            self.header['bits'].target, 
+            self.share_info['timestamp'], 
+            self.share_info['bits'].target, 
+            self.contents['ref_merkle_link'], 
+            [(h, None) for h in other_tx_hashes], 
+            self.net, 
+            last_txout_nonce=self.contents['last_txout_nonce'],
+            base_subsidy=None
+        )
+
         assert other_tx_hashes2 == other_tx_hashes
         if share_info != self.share_info:
+            print share_info, self.share_info
             raise ValueError('share_info invalid')
         if bitcoin_data.hash256(bitcoin_data.tx_type.pack(gentx)) != self.gentx_hash:
             raise ValueError('''gentx doesn't match hash_link''')
@@ -365,7 +433,7 @@ class Share(object):
         other_txs = self._get_other_txs(tracker, known_txs)
         if other_txs is None:
             return None # not all txs present
-        return dict(header=self.header, txs=[self.check(tracker)] + other_txs)
+        return dict(header=self.header, txs=[self.check(tracker)] + other_txs, signature='')
 
 
 class WeightsSkipList(forest.TrackerSkipList):
@@ -570,18 +638,33 @@ def get_stale_counts(tracker, share_hash, lookbehind, rates=False):
 def get_user_stale_props(tracker, share_hash, lookbehind):
     res = {}
     for share in tracker.get_chain(share_hash, lookbehind - 1):
-        stale, total = res.get(share.share_data['pubkey_hash'], (0, 0))
+        stale, total = res.get(share.share_data['pubkey'], (0, 0))
         total += 1
         if share.share_data['stale_info'] is not None:
             stale += 1
             total += 1
-        res[share.share_data['pubkey_hash']] = stale, total
-    return dict((pubkey_hash, stale/total) for pubkey_hash, (stale, total) in res.iteritems())
+        res[share.share_data['pubkey']] = stale, total
+    return dict((pubkey, stale/total) for pubkey, (stale, total) in res.iteritems())
+
+def calculate_payout(weight, total_weight, subsidy):
+    global minout
+
+    payout = (subsidy - 3*minout) * weight//total_weight
+
+    if payout < minout:
+        payout = 0
+
+    return payout
 
 def get_expected_payouts(tracker, best_share_hash, block_target, subsidy, net):
+
     weights, total_weight, donation_weight = tracker.get_cumulative_weights(best_share_hash, min(tracker.get_height(best_share_hash), net.REAL_CHAIN_LENGTH), 65535*net.SPREAD*bitcoin_data.target_to_average_attempts(block_target))
-    res = dict((script, subsidy*weight//total_weight) for script, weight in weights.iteritems())
+
+    #res = dict((script, subsidy*weight//total_weight) for script, weight in weights.iteritems())
+
+    res = dict((script, calculate_payout(weight, total_weight, subsidy)) for script, weight in weights.iteritems())
     res[DONATION_SCRIPT] = res.get(DONATION_SCRIPT, 0) + subsidy - sum(res.itervalues())
+
     return res
 
 def get_desired_version_counts(tracker, best_share_hash, dist):
index 0e2c743..efc4248 100644 (file)
@@ -14,7 +14,7 @@ import urlparse
 if '--iocp' in sys.argv:
     from twisted.internet import iocpreactor
     iocpreactor.install()
-from twisted.internet import defer, reactor, protocol, task, tcp
+from twisted.internet import defer, reactor, protocol, task
 from twisted.web import server
 from twisted.python import log
 from nattraverso import portmapper, ipdiscover
@@ -69,32 +69,33 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
             factory = yield connect_p2p()
         
         print 'Determining payout address...'
-        if args.pubkey_hash is None:
-            address_path = os.path.join(datadir_path, 'cached_payout_address')
-            
-            if os.path.exists(address_path):
-                with open(address_path, 'rb') as f:
-                    address = f.read().strip('\r\n')
-                print '    Loaded cached address: %s...' % (address,)
-            else:
-                address = None
-            
-            if address is not None:
-                res = yield deferral.retry('Error validating cached address:', 5)(lambda: bitcoind.rpc_validateaddress(address))()
+        pubkey_path = os.path.join(datadir_path, 'cached_payout_pubkey')
+        
+        if os.path.exists(pubkey_path):
+            with open(pubkey_path, 'rb') as f:
+                pubkey = f.read().strip('\r\n')
+            print '    Loaded cached pubkey, payout address: %s...' % (bitcoin_data.pubkey_to_address(pubkey.decode('hex'), net.PARENT),)
+        else:
+            pubkey = None
+
+        if pubkey is not None:
+                res = yield deferral.retry('Error validating cached pubkey:', 5)(lambda: bitcoind.rpc_validatepubkey(pubkey))()
                 if not res['isvalid'] or not res['ismine']:
-                    print '    Cached address is either invalid or not controlled by local bitcoind!'
+                    print '    Cached pubkey is either invalid or not controlled by local bitcoind!'
                     address = None
+
+        if pubkey is None:
+            print '    Getting payout pubkey from bitcoind...'
+            pubkey = yield deferral.retry('Error getting payout pubkey from bitcoind:', 5)(lambda: bitcoind.rpc_getnewpubkey('p2pool'))()
             
-            if address is None:
-                print '    Getting payout address from bitcoind...'
-                address = yield deferral.retry('Error getting payout address from bitcoind:', 5)(lambda: bitcoind.rpc_getaccountaddress('p2pool'))()
-            
-            with open(address_path, 'wb') as f:
-                f.write(address)
-            
-            my_pubkey_hash = bitcoin_data.address_to_pubkey_hash(address, net.PARENT)
-        else:
-            my_pubkey_hash = args.pubkey_hash
+            with open(pubkey_path, 'wb') as f:
+                f.write(pubkey)
+
+        my_pubkey = pubkey.decode('hex')
+
+        address = bitcoin_data.pubkey_to_address(my_pubkey, net.PARENT)
+
+        my_pubkey_hash = bitcoin_data.address_to_pubkey_hash(address, net.PARENT)
         print '    ...success! Payout address:', bitcoin_data.pubkey_hash_to_address(my_pubkey_hash, net.PARENT)
         print
         
@@ -212,7 +213,7 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
         
         print 'Listening for workers on %r port %i...' % (worker_endpoint[0], worker_endpoint[1])
         
-        wb = work.WorkerBridge(node, my_pubkey_hash, args.donation_percentage, merged_urls, args.worker_fee)
+        wb = work.WorkerBridge(node, my_pubkey, args.donation_percentage, merged_urls, args.worker_fee)
         web_root = web.get_web_root(wb, datadir_path, bitcoind_warning_var)
         caching_wb = worker_interface.CachingWorkerBridge(wb)
         worker_interface.WorkerInterface(caching_wb).attach_to(web_root, get_handler=lambda request: request.redirect('/static/'))
@@ -327,7 +328,7 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
                             shares, stale_orphan_shares, stale_doa_shares,
                             math.format_binomial_conf(stale_orphan_shares + stale_doa_shares, shares, 0.95),
                             math.format_binomial_conf(stale_orphan_shares + stale_doa_shares, shares, 0.95, lambda x: (1 - x)/(1 - stale_prop)),
-                            node.get_current_txouts().get(bitcoin_data.pubkey_hash_to_script2(my_pubkey_hash), 0)*1e-8, net.PARENT.SYMBOL,
+                            node.get_current_txouts().get(bitcoin_data.pubkey_to_script2(my_pubkey), 0) * 1e-6, net.PARENT.SYMBOL,
                         )
                         this_str += '\n Pool: %sH/s Stale rate: %.1f%% Expected time to block: %s' % (
                             math.format(int(real_att_s)),
@@ -355,11 +356,6 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
         log.err(None, 'Fatal error:')
 
 def run():
-    if not hasattr(tcp.Client, 'abortConnection'):
-        print "Twisted doesn't have abortConnection! Upgrade to a newer version of Twisted to avoid memory leaks!"
-        print 'Pausing for 3 seconds...'
-        time.sleep(3)
-    
     realnets = dict((name, net) for name, net in networks.nets.iteritems() if '_testnet' not in name)
     
     parser = fixargparse.FixedArgumentParser(description='p2pool (version %s)' % (p2pool.__version__,), fromfile_prefix_chars='@')
@@ -512,15 +508,7 @@ def run():
     else:
         addr, port = args.worker_endpoint.rsplit(':', 1)
         worker_endpoint = addr, int(port)
-    
-    if args.address is not None:
-        try:
-            args.pubkey_hash = bitcoin_data.address_to_pubkey_hash(args.address, net.PARENT)
-        except Exception, e:
-            parser.error('error parsing address: ' + repr(e))
-    else:
-        args.pubkey_hash = None
-    
+
     def separate_url(url):
         s = urlparse.urlsplit(url)
         if '@' not in s.netloc:
index 892da6a..78439e3 100644 (file)
@@ -8,117 +8,42 @@ from p2pool.util import math
 # changes can be done by changing one, then the other
 
 nets = dict(
-    bitcoin=math.Object(
-        PARENT=networks.nets['bitcoin'],
+    novacoin=math.Object(
+        PARENT=networks.nets['novacoin'],
         SHARE_PERIOD=10, # seconds
         CHAIN_LENGTH=24*60*60//10, # shares
         REAL_CHAIN_LENGTH=24*60*60//10, # shares
         TARGET_LOOKBEHIND=200, # shares
         SPREAD=3, # blocks
-        IDENTIFIER='fc70035c7a81bc6f'.decode('hex'),
-        PREFIX='2472ef181efcd37b'.decode('hex'),
-        P2P_PORT=9333,
-        MIN_TARGET=0,
-        MAX_TARGET=2**256//2**32 - 1,
-        PERSIST=True,
-        WORKER_PORT=9332,
-        BOOTSTRAP_ADDRS='forre.st vps.forre.st 74.220.242.6:9334 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'.split(' '),
-        ANNOUNCE_CHANNEL='#p2pool',
-        VERSION_CHECK=lambda v: 50700 <= v < 60000 or 60010 <= v < 60100 or 60400 <= v,
-    ),
-    bitcoin_testnet=math.Object(
-        PARENT=networks.nets['bitcoin_testnet'],
-        SHARE_PERIOD=10, # seconds
-        CHAIN_LENGTH=60*60//10, # shares
-        REAL_CHAIN_LENGTH=60*60//10, # shares
-        TARGET_LOOKBEHIND=200, # shares
-        SPREAD=3, # blocks
-        IDENTIFIER='5fc2be2d4f0d6bfb'.decode('hex'),
-        PREFIX='3f6057a15036f441'.decode('hex'),
-        P2P_PORT=19333,
-        MIN_TARGET=0,
-        MAX_TARGET=2**256//2**32 - 1,
-        PERSIST=False,
-        WORKER_PORT=19332,
-        BOOTSTRAP_ADDRS='forre.st vps.forre.st liteco.in'.split(' '),
-        ANNOUNCE_CHANNEL='#p2pool-alt',
-        VERSION_CHECK=lambda v: 50700 <= v < 60000 or 60010 <= v < 60100 or 60400 <= v,
-    ),
-    
-    litecoin=math.Object(
-        PARENT=networks.nets['litecoin'],
-        SHARE_PERIOD=10, # seconds
-        CHAIN_LENGTH=24*60*60//10, # shares
-        REAL_CHAIN_LENGTH=24*60*60//10, # shares
-        TARGET_LOOKBEHIND=200, # shares
-        SPREAD=12, # blocks
-        IDENTIFIER='e037d5b8c6923410'.decode('hex'),
-        PREFIX='7208c1a53ef629b0'.decode('hex'),
-        P2P_PORT=9338,
+        IDENTIFIER='e037d5b8c6923510'.decode('hex'),
+        PREFIX='7208c1a53ef649b0'.decode('hex'),
+        P2P_PORT=9777,
         MIN_TARGET=0,
         MAX_TARGET=2**256//2**20 - 1,
         PERSIST=True,
-        WORKER_PORT=9327,
-        BOOTSTRAP_ADDRS='forre.st vps.forre.st 199.255.95.94 75.12.89.18 181.28.244.151 83.142.189.132 66.90.82.155:11332 201.57.241.77 80.222.255.91 142.68.214.29 24.52.247.82 72.230.179.177 94.127.200.29 200.204.161.215 91.121.9.7 91.235.254.37 198.154.98.195 178.79.136.10'.split(' '),
+        WORKER_PORT=8336,
+        BOOTSTRAP_ADDRS='188.120.239.144'.split(' '),
         ANNOUNCE_CHANNEL='#p2pool-alt',
         VERSION_CHECK=lambda v: True,
     ),
-    litecoin_testnet=math.Object(
-        PARENT=networks.nets['litecoin_testnet'],
+    novacoin_testnet=math.Object(
+        PARENT=networks.nets['novacoin_testnet'],
         SHARE_PERIOD=3, # seconds
         CHAIN_LENGTH=20*60//3, # shares
         REAL_CHAIN_LENGTH=20*60//3, # shares
         TARGET_LOOKBEHIND=200, # shares
         SPREAD=12, # blocks
-        IDENTIFIER='cca5e24ec6408b1e'.decode('hex'),
-        PREFIX='ad9614f6466a39cf'.decode('hex'),
-        P2P_PORT=19338,
+        IDENTIFIER='e037d5b8c7923510'.decode('hex'),
+        PREFIX='7208c1a54ef649b0'.decode('hex'),
+        P2P_PORT=19777,
         MIN_TARGET=0,
-        MAX_TARGET=2**256//2000 - 1,
-        PERSIST=False,
-        WORKER_PORT=19327,
-        BOOTSTRAP_ADDRS='forre.st vps.forre.st'.split(' '),
-        ANNOUNCE_CHANNEL='#p2pool-alt',
-        VERSION_CHECK=lambda v: True,
-    ),
-
-    terracoin=math.Object(
-        PARENT=networks.nets['terracoin'],
-        SHARE_PERIOD=30, # seconds
-        CHAIN_LENGTH=24*60*60//30, # shares
-        REAL_CHAIN_LENGTH=24*60*60//30, # shares
-        TARGET_LOOKBEHIND=200, # shares
-        SPREAD=15, # blocks
-        IDENTIFIER='a41b2356a1b7d35e'.decode('hex'),
-        PREFIX='5623b62178d2b8a3'.decode('hex'),
-        P2P_PORT=9323,
-        MIN_TARGET=0,
-        MAX_TARGET=2**256//2**32 - 1,
-        PERSIST=True,
-        WORKER_PORT=9322,
-        BOOTSTRAP_ADDRS='seed1.p2pool.terracoin.org seed2.p2pool.terracoin.org forre.st vps.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'.split(' '),
-        ANNOUNCE_CHANNEL='#p2pool-alt',
-        VERSION_CHECK=lambda v: True,
-    ),
-    terracoin_testnet=math.Object(
-        PARENT=networks.nets['terracoin_testnet'],
-        SHARE_PERIOD=30, # seconds
-        CHAIN_LENGTH=60*60//30, # shares
-        REAL_CHAIN_LENGTH=60*60//30, # shares
-        TARGET_LOOKBEHIND=200, # shares
-        SPREAD=15, # blocks
-        IDENTIFIER='b41b2356a5b7d35d'.decode('hex'),
-        PREFIX='1623b92172d2b8a2'.decode('hex'),
-        P2P_PORT=19323,
-        MIN_TARGET=0,
-        MAX_TARGET=2**256//2**32 - 1,
+        MAX_TARGET=2**256//2**20 - 1,
         PERSIST=False,
-        WORKER_PORT=19322,
-        BOOTSTRAP_ADDRS='seed1.p2pool.terracoin.org seed2.p2pool.terracoin.org forre.st vps.forre.st'.split(' '),
+        WORKER_PORT=18336,
+        BOOTSTRAP_ADDRS='188.120.239.144'.split(' '),
         ANNOUNCE_CHANNEL='#p2pool-alt',
         VERSION_CHECK=lambda v: True,
     ),
-
 )
 for net_name, net in nets.iteritems():
     net.NAME = net_name
index 1c98866..9b21768 100644 (file)
@@ -195,10 +195,10 @@ class Node(object):
             if (self.best_block_header.value is None
                 or (
                     new_header['previous_block'] == bitcoind_best_block and
-                    bitcoin_data.hash256(bitcoin_data.block_header_type.pack(self.best_block_header.value)) == bitcoind_best_block
+                    self.net.PARENT.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(self.best_block_header.value)) == bitcoind_best_block
                 ) # new is child of current and previous is current
                 or (
-                    bitcoin_data.hash256(bitcoin_data.block_header_type.pack(new_header)) == bitcoind_best_block and
+                    self.net.PARENT.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(new_header)) == bitcoind_best_block and
                     self.best_block_header.value['previous_block'] != bitcoind_best_block
                 )): # new is current and previous is not a child of current
                 self.best_block_header.set(new_header)
@@ -252,9 +252,11 @@ class Node(object):
         
         @self.tracker.verified.added.watch
         def _(share):
+            if share.timestamp < share.min_header['timestamp']:
+                return
             if not (share.pow_hash <= share.header['bits'].target):
                 return
-            
+
             block = share.as_block(self.tracker, self.known_txs_var.value)
             if block is None:
                 print >>sys.stderr, 'GOT INCOMPLETE BLOCK FROM PEER! %s bitcoin: %s%064x' % (p2pool_data.format_hash(share.hash), self.net.PARENT.BLOCK_EXPLORER_URL_PREFIX, share.header_hash)
index c3f7719..5b39542 100644 (file)
@@ -143,7 +143,7 @@ class MiniNode(object):
         self.n.p2p_node = node.P2PNode(self.n, port=0, max_incoming_conns=1000000, addr_store={}, connect_addrs=[('127.0.0.1', peer_port) for peer_port in peer_ports])
         self.n.p2p_node.start()
         
-        wb = work.WorkerBridge(node=self.n, my_pubkey_hash=random.randrange(2**160), donation_percentage=random.uniform(0, 10), merged_urls=merged_urls, worker_fee=3)
+        wb = work.WorkerBridge(node=self.n, my_pubkey=random.randrange(2**520), donation_percentage=random.uniform(0, 10), merged_urls=merged_urls, worker_fee=3)
         self.wb = wb
         web_root = resource.Resource()
         worker_interface.WorkerInterface(wb).attach_to(web_root)
@@ -170,7 +170,7 @@ class Test(unittest.TestCase):
         n = node.Node(bitd, bitd, [], [], mynet)
         yield n.start()
         
-        wb = work.WorkerBridge(node=n, my_pubkey_hash=42, donation_percentage=2, merged_urls=[('http://127.0.0.1:%i' % (mm_port.getHost().port,), '')], worker_fee=3)
+        wb = work.WorkerBridge(node=n, my_pubkey=42, donation_percentage=2, merged_urls=[('http://127.0.0.1:%i' % (mm_port.getHost().port,), '')], worker_fee=3)
         web_root = resource.Resource()
         worker_interface.WorkerInterface(wb).attach_to(web_root)
         port = reactor.listenTCP(0, server.Site(web_root))
index d810ada..bf11813 100644 (file)
@@ -129,7 +129,7 @@ def _http_do(url, headers, timeout, method, params):
     
     if resp['id'] != id_:
         raise ValueError('invalid id')
-    if 'error' in resp and resp['error'] is not None:
+    if 'error' in resp and resp['error'] is not None and resp['error']['code'] != -100:
         raise Error_for_code(resp['error']['code'])(resp['error']['message'], resp['error'].get('data', None))
     defer.returnValue(resp['result'])
 HTTPProxy = lambda url, headers={}, timeout=5: Proxy(lambda method, params: _http_do(url, headers, timeout, method, params))
index c401e82..cc80a07 100644 (file)
@@ -176,7 +176,7 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve
             uptime=time.time() - start_time,
             attempts_to_share=bitcoin_data.target_to_average_attempts(node.tracker.items[node.best_share_var.value].max_target),
             attempts_to_block=bitcoin_data.target_to_average_attempts(node.bitcoind_work.value['bits'].target),
-            block_value=node.bitcoind_work.value['subsidy']*1e-8,
+            block_value=node.bitcoind_work.value['subsidy']*1e-6,
             warnings=p2pool_data.get_warnings(node.tracker, node.best_share_var.value, node.net, bitcoind_warning_var.value, node.bitcoind_work.value),
             donation_proportion=wb.donation_percentage/100,
         )
@@ -195,13 +195,13 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve
             request.setHeader('Access-Control-Allow-Origin', '*')
             res = yield self.func(*self.args)
             defer.returnValue(json.dumps(res) if self.mime_type == 'application/json' else res)
-    
+
     def decent_height():
         return min(node.tracker.get_height(node.best_share_var.value), 720)
     web_root.putChild('rate', WebInterface(lambda: p2pool_data.get_pool_attempts_per_second(node.tracker, node.best_share_var.value, decent_height())/(1-p2pool_data.get_average_stale_prop(node.tracker, node.best_share_var.value, decent_height()))))
     web_root.putChild('difficulty', WebInterface(lambda: bitcoin_data.target_to_difficulty(node.tracker.items[node.best_share_var.value].max_target)))
     web_root.putChild('users', WebInterface(get_users))
-    web_root.putChild('user_stales', WebInterface(lambda: dict((bitcoin_data.pubkey_hash_to_address(ph, node.net.PARENT), prop) for ph, prop in
+    web_root.putChild('user_stales', WebInterface(lambda: dict((bitcoin_data.pubkey_to_address(ph, node.net.PARENT), prop) for ph, prop in
         p2pool_data.get_user_stale_props(node.tracker, node.best_share_var.value, node.tracker.get_height(node.best_share_var.value)).iteritems())))
     web_root.putChild('fee', WebInterface(lambda: wb.worker_fee))
     web_root.putChild('current_payouts', WebInterface(lambda: dict((bitcoin_data.script2_to_address(script, node.net.PARENT), value/1e8) for script, value in node.get_current_txouts().iteritems())))
@@ -221,7 +221,7 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve
         ])
     ))))
     web_root.putChild('peer_versions', WebInterface(lambda: dict(('%s:%i' % peer.addr, peer.other_sub_version) for peer in node.p2p_node.peers.itervalues())))
-    web_root.putChild('payout_addr', WebInterface(lambda: bitcoin_data.pubkey_hash_to_address(wb.my_pubkey_hash, node.net.PARENT)))
+    web_root.putChild('payout_addr', WebInterface(lambda: bitcoin_data.pubkey_to_address(wb.my_pubkey, node.net.PARENT)))
     web_root.putChild('recent_blocks', WebInterface(lambda: [dict(
         ts=s.timestamp,
         hash='%064x' % s.header_hash,
@@ -262,14 +262,14 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve
             shares=shares,
             stale_shares=stale_orphan_shares + stale_doa_shares,
             stale_shares_breakdown=dict(orphan=stale_orphan_shares, doa=stale_doa_shares),
-            current_payout=node.get_current_txouts().get(bitcoin_data.pubkey_hash_to_script2(wb.my_pubkey_hash), 0)*1e-8,
+            current_payout=node.get_current_txouts().get(bitcoin_data.pubkey_to_script2(wb.my_pubkey), 0)*1e-6,
             peers=dict(
                 incoming=sum(1 for peer in node.p2p_node.peers.itervalues() if peer.incoming),
                 outgoing=sum(1 for peer in node.p2p_node.peers.itervalues() if not peer.incoming),
             ),
             attempts_to_share=bitcoin_data.target_to_average_attempts(node.tracker.items[node.best_share_var.value].max_target),
             attempts_to_block=bitcoin_data.target_to_average_attempts(node.bitcoind_work.value['bits'].target),
-            block_value=node.bitcoind_work.value['subsidy']*1e-8,
+            block_value=node.bitcoind_work.value['subsidy']*1e-6,
         ))
         
         with open(os.path.join(datadir_path, 'stats'), 'wb') as f:
@@ -316,7 +316,7 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve
                 gentx=dict(
                     hash='%064x' % share.gentx_hash,
                     coinbase=share.share_data['coinbase'].ljust(2, '\x00').encode('hex'),
-                    value=share.share_data['subsidy']*1e-8,
+                    value=share.share_data['subsidy']*1e-6,
                 ),
                 txn_count_range=[len(share.other_txs), len(share.other_txs)] if share.other_txs is not None else 1 if len(share.merkle_link['branch']) == 0 else [2**len(share.merkle_link['branch'])//2+1, 2**len(share.merkle_link['branch'])],
             ),
@@ -428,10 +428,10 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve
         hd.datastreams['pool_rates'].add_datum(t, pool_rates)
         
         current_txouts = node.get_current_txouts()
-        hd.datastreams['current_payout'].add_datum(t, current_txouts.get(bitcoin_data.pubkey_hash_to_script2(wb.my_pubkey_hash), 0)*1e-8)
+        hd.datastreams['current_payout'].add_datum(t, current_txouts.get(bitcoin_data.pubkey_to_script2(wb.my_pubkey), 0)*1e-6)
         miner_hash_rates, miner_dead_hash_rates = get_local_rates()
         current_txouts_by_address = dict((bitcoin_data.script2_to_address(script, node.net.PARENT), amount) for script, amount in current_txouts.iteritems())
-        hd.datastreams['current_payouts'].add_datum(t, dict((user, current_txouts_by_address[user]*1e-8) for user in miner_hash_rates if user in current_txouts_by_address))
+        hd.datastreams['current_payouts'].add_datum(t, dict((user, current_txouts_by_address[user]*1e-6) for user in miner_hash_rates if user in current_txouts_by_address))
         
         hd.datastreams['incoming_peers'].add_datum(t, sum(1 for peer in node.p2p_node.peers.itervalues() if peer.incoming))
         hd.datastreams['outgoing_peers'].add_datum(t, sum(1 for peer in node.p2p_node.peers.itervalues() if not peer.incoming))
index 318c1d9..2126839 100644 (file)
@@ -16,12 +16,12 @@ import p2pool, p2pool.data as p2pool_data
 class WorkerBridge(worker_interface.WorkerBridge):
     COINBASE_NONCE_LENGTH = 4
     
-    def __init__(self, node, my_pubkey_hash, donation_percentage, merged_urls, worker_fee):
+    def __init__(self, node, my_pubkey, donation_percentage, merged_urls, worker_fee):
         worker_interface.WorkerBridge.__init__(self)
         self.recent_shares_ts_work = []
         
         self.node = node
-        self.my_pubkey_hash = my_pubkey_hash
+        self.my_pubkey = my_pubkey
         self.donation_percentage = donation_percentage
         self.worker_fee = worker_fee
         
@@ -80,16 +80,20 @@ class WorkerBridge(worker_interface.WorkerBridge):
         
         # COMBINE WORK
         
+
         self.current_work = variable.Variable(None)
         def compute_work():
             t = self.node.bitcoind_work.value
             bb = self.node.best_block_header.value
+
+            subsidy = self.node.net.PARENT.SUBSIDY_FUNC(bb['bits'].target)
+
             if bb is not None and bb['previous_block'] == t['previous_block'] and self.node.net.PARENT.POW_FUNC(bitcoin_data.block_header_type.pack(bb)) <= t['bits'].target:
                 print 'Skipping from block %x to block %x!' % (bb['previous_block'],
-                    bitcoin_data.hash256(bitcoin_data.block_header_type.pack(bb)))
+                    self.node.net.PARENT.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(bb)))
                 t = dict(
                     version=bb['version'],
-                    previous_block=bitcoin_data.hash256(bitcoin_data.block_header_type.pack(bb)),
+                    previous_block=self.node.net.PARENT.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(bb)),
                     bits=bb['bits'], # not always true
                     coinbaseflags='',
                     height=t['height'] + 1,
@@ -97,7 +101,7 @@ class WorkerBridge(worker_interface.WorkerBridge):
                     transactions=[],
                     transaction_fees=[],
                     merkle_link=bitcoin_data.calculate_merkle_link([None], 0),
-                    subsidy=self.node.net.PARENT.SUBSIDY_FUNC(self.node.bitcoind_work.value['height']),
+                    subsidy=subsidy,
                     last_update=self.node.bitcoind_work.value['last_update'],
                 )
             
@@ -150,21 +154,15 @@ class WorkerBridge(worker_interface.WorkerBridge):
             except:
                 pass
         
-        if random.uniform(0, 100) < self.worker_fee:
-            pubkey_hash = self.my_pubkey_hash
-        else:
-            try:
-                pubkey_hash = bitcoin_data.address_to_pubkey_hash(user, self.node.net.PARENT)
-            except: # XXX blah
-                pubkey_hash = self.my_pubkey_hash
+        pubkey = self.my_pubkey
         
-        return user, pubkey_hash, desired_share_target, desired_pseudoshare_target
+        return user, pubkey, desired_share_target, desired_pseudoshare_target
     
     def preprocess_request(self, user):
-        user, pubkey_hash, desired_share_target, desired_pseudoshare_target = self.get_user_details(user)
-        return pubkey_hash, desired_share_target, desired_pseudoshare_target
+        user, pubkey, desired_share_target, desired_pseudoshare_target = self.get_user_details(user)
+        return pubkey, desired_share_target, desired_pseudoshare_target
     
-    def get_work(self, pubkey_hash, desired_share_target, desired_pseudoshare_target):
+    def get_work(self, pubkey, desired_share_target, desired_pseudoshare_target):
         if (self.node.p2p_node is None or len(self.node.p2p_node.peers) == 0) and self.node.net.PERSIST:
             raise jsonrpc.Error_for_code(-12345)(u'p2pool is not connected to any peers')
         if self.node.best_share_var.value is None and self.node.net.PERSIST:
@@ -208,6 +206,8 @@ class WorkerBridge(worker_interface.WorkerBridge):
                     share_type = previous_share_type
         
         if True:
+            subsidy = self.node.net.PARENT.SUBSIDY_FUNC(self.current_work.value['bits'].target)
+
             share_info, gentx, other_transaction_hashes, get_share = share_type.generate_transaction(
                 tracker=self.node.tracker,
                 share_data=dict(
@@ -217,7 +217,7 @@ class WorkerBridge(worker_interface.WorkerBridge):
                         ] + ([mm_data] if mm_data else []) + [
                     ]) + self.current_work.value['coinbaseflags'])[:100],
                     nonce=random.randrange(2**32),
-                    pubkey_hash=pubkey_hash,
+                    pubkey=pubkey,
                     subsidy=self.current_work.value['subsidy'],
                     donation=math.perfect_round(65535*self.donation_percentage/100),
                     stale_info=(lambda (orphans, doas), total, (orphans_recorded_in_chain, doas_recorded_in_chain):
@@ -234,7 +234,7 @@ class WorkerBridge(worker_interface.WorkerBridge):
                 desired_other_transaction_hashes_and_fees=zip(tx_hashes, self.current_work.value['transaction_fees']),
                 net=self.node.net,
                 known_txs=tx_map,
-                base_subsidy=self.node.net.PARENT.SUBSIDY_FUNC(self.current_work.value['height']),
+                base_subsidy=subsidy
             )
         
         packed_gentx = bitcoin_data.tx_type.pack(gentx)
@@ -262,7 +262,7 @@ class WorkerBridge(worker_interface.WorkerBridge):
         print 'New work for worker! Difficulty: %.06f Share difficulty: %.06f Total block value: %.6f %s including %i transactions' % (
             bitcoin_data.target_to_difficulty(target),
             bitcoin_data.target_to_difficulty(share_info['bits'].target),
-            self.current_work.value['subsidy']*1e-8, self.node.net.PARENT.SYMBOL,
+            self.current_work.value['subsidy']*1e-6, self.node.net.PARENT.SYMBOL,
             len(self.current_work.value['transactions']),
         )
         
@@ -272,7 +272,7 @@ class WorkerBridge(worker_interface.WorkerBridge):
             merkle_link=merkle_link,
             coinb1=packed_gentx[:-4-4],
             coinb2=packed_gentx[-4:],
-            timestamp=self.current_work.value['time'],
+            timestamp=gentx['timestamp'],
             bits=self.current_work.value['bits'],
             share_target=target,
         )
@@ -284,11 +284,26 @@ class WorkerBridge(worker_interface.WorkerBridge):
             new_packed_gentx = packed_gentx[:-4-4] + coinbase_nonce + packed_gentx[-4:] if coinbase_nonce != '\0'*self.COINBASE_NONCE_LENGTH else packed_gentx
             new_gentx = bitcoin_data.tx_type.unpack(new_packed_gentx) if coinbase_nonce != '\0'*self.COINBASE_NONCE_LENGTH else gentx
             
-            header_hash = bitcoin_data.hash256(bitcoin_data.block_header_type.pack(header))
+            header_hash = self.node.net.PARENT.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(header))
             pow_hash = self.node.net.PARENT.POW_FUNC(bitcoin_data.block_header_type.pack(header))
             try:
+                if header['timestamp'] > new_gentx['timestamp'] + 3600:
+                    print 
+                    print header['timestamp'], '>', new_gentx['timestamp'] + 3600
+                    print 'Coinbase timestamp is too early!'
+                    print 
+
+                    return
+
+                if header['timestamp'] < new_gentx['timestamp']:
+                    print 
+                    print header['timestamp'], '<', new_gentx['timestamp']
+                    print 'Block header timestamp is before coinbase timestamp!'
+                    print 
+                    return
+
                 if pow_hash <= header['bits'].target or p2pool.DEBUG:
-                    helper.submit_block(dict(header=header, txs=[new_gentx] + other_transactions), False, self.node.factory, self.node.bitcoind, self.node.bitcoind_work, self.node.net)
+                    helper.submit_block(dict(header=header, txs=[new_gentx] + other_transactions, signature=''), False, self.node.factory, self.node.bitcoind, self.node.bitcoind_work, self.node.net)
                     if pow_hash <= header['bits'].target:
                         print
                         print 'GOT BLOCK FROM MINER! Passing to bitcoind! %s%064x' % (self.node.net.PARENT.BLOCK_EXPLORER_URL_PREFIX, header_hash)