7 logging.basicConfig(level=logging.DEBUG)
9 def RaiseRedFlags(reason):
10 logging.getLogger('redflag').critical(reason)
14 from bitcoin.node import BitcoinLink
15 UpstreamBitcoind = BitcoinLink( config.UpstreamBitcoindNode, config.UpstreamNetworkId )
18 from bitcoin.script import BitcoinScript
19 from bitcoin.txn import Txn
20 from base58 import b58decode
21 from struct import pack
25 def makeCoinbaseTxn(coinbaseValue, useCoinbaser = True):
28 if useCoinbaser and hasattr(config, 'CoinbaserCmd'):
31 cmd = config.CoinbaserCmd
32 cmd = cmd.replace('%d', str(coinbaseValue))
33 p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
34 nout = int(p.stdout.readline())
36 amount = int(p.stdout.readline())
37 addr = p.stdout.readline().rstrip(b'\n').decode('utf8')
38 pkScript = BitcoinScript.toAddress(addr)
39 t.addOutput(amount, pkScript)
42 coinbased = coinbaseValue + 1
43 if coinbased >= coinbaseValue:
44 logging.getLogger('makeCoinbaseTxn').error('Coinbaser failed!')
47 coinbaseValue -= coinbased
49 pkScript = BitcoinScript.toAddress(config.TrackerAddr)
50 t.addOutput(coinbaseValue, pkScript)
53 # TODO: red flag on dupe coinbase
57 from util import Bits2Target
63 global MM, networkTarget
64 networkTarget = Bits2Target(MM.currentBlock[1])
68 from merklemaker import merkleMaker
70 MM.__dict__.update(config.__dict__)
71 MM.clearCoinbaseTxn = makeCoinbaseTxn(5000000000, False) # FIXME
72 MM.clearCoinbaseTxn.assemble()
73 MM.makeCoinbaseTxn = makeCoinbaseTxn
74 MM.onBlockChange = blockChanged
75 MM._THISISUGLY = UpstreamBitcoind
79 from binascii import b2a_hex
80 from struct import pack, unpack
83 from util import RejectedShare, dblsha, hash2int
86 if hasattr(config, 'DbOptions'):
88 db = psycopg2.connect(**config.DbOptions)
90 def getBlockHeader(username):
92 (merkleRoot, merkleTree, coinbase, prevBlock, bits, rollPrevBlk) = MRD
93 timestamp = pack('<L', int(time()))
94 hdr = b'\1\0\0\0' + prevBlock + merkleRoot + timestamp + bits + b'iolE'
95 workLog.setdefault(username, {})[merkleRoot] = MRD
101 return 'Y' if b else 'N'
107 rem_host = share.get('remoteHost', '?')
108 username = share['username']
109 reason = share.get('rejectReason', None)
110 upstreamResult = share.get('upstreamResult', None)
111 solution = share['data']
112 solution = b2a_hex(solution).decode('utf8')
113 stmt = "insert into shares (rem_host, username, our_result, upstream_result, reason, solution) values (%s, %s, %s, %s, %s, decode(%s, 'hex'))"
114 params = (rem_host, username, YN(not reason), YN(upstreamResult), reason, solution)
116 dbc.execute(stmt, params)
119 def checkShare(share):
121 (prevBlock, bits) = MM.currentBlock
122 sharePrevBlock = data[4:36]
123 if sharePrevBlock != prevBlock:
124 if sharePrevBlock == MM.lastBlock[0]:
125 raise RejectedShare('stale-prevblk')
126 raise RejectedShare('bad-prevblk')
128 shareMerkleRoot = data[36:68]
130 username = share['username']
131 if username not in workLog:
132 raise RejectedShare('unknown-user')
133 MWL = workLog[username]
134 if shareMerkleRoot not in MWL:
135 raise RejectedShare('unknown-work')
136 MRD = MWL[shareMerkleRoot]
139 shareTimestamp = unpack('<L', data[68:72])[0]
140 shareTime = share['time'] = time()
141 if shareTimestamp < shareTime - 300:
142 raise RejectedShare('time-too-old')
143 if shareTimestamp > shareTime + 7200:
144 raise RejectedShare('time-too-new')
145 if data[72:76] != bits:
146 raise RejectedShare('bad-diffbits')
147 if data[:4] != b'\1\0\0\0':
148 raise RejectedShare('bad-version')
150 blkhash = dblsha(data)
151 if blkhash[28:] != b'\0\0\0\0':
152 raise RejectedShare('H-not-zero')
153 blkhashn = hash2int(blkhash)
156 logfunc = getattr(checkShare.logger, 'info' if blkhashn <= networkTarget else 'debug')
157 logfunc('BLKHASH: %64x' % (blkhashn,))
158 logfunc(' TARGET: %64x' % (networkTarget,))
160 if blkhashn <= networkTarget:
161 logfunc("Submitting upstream")
164 t.setCoinbase(MRD[2])
166 UpstreamBitcoind.submitBlock(data, txlist)
167 share['upstreamResult'] = True
170 checkShare.logger = logging.getLogger('checkShare')
172 def receiveShare(share):
173 # TODO: username => userid
177 def newBlockNotification():
178 MM.updateMerkleTree()
179 # TODO: Force RESPOND TO LONGPOLLS?
182 def newBlockNotificationSIGNAL(signum, frame):
183 # Use a new thread, in case the signal handler is called with locks held
184 thr = threading.Thread(target=newBlockNotification, name='newBlockNotification via signal %s' % (signum,))
188 from signal import signal, SIGUSR1
189 signal(SIGUSR1, newBlockNotificationSIGNAL)
192 from jsonrpcserver import JSONRPCServer
193 import interactivemode
195 if __name__ == "__main__":
196 server = JSONRPCServer(('', 8444))
197 server.getBlockHeader = getBlockHeader
198 server.receiveShare = receiveShare
199 server.RaiseRedFlags = RaiseRedFlags
200 server.serve_forever()