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
71 global MM, networkTarget, server
72 networkTarget = Bits2Target(MM.currentBlock[1])
77 from merklemaker import merkleMaker
79 MM.__dict__.update(config.__dict__)
80 MM.clearCoinbaseTxn = makeCoinbaseTxn(5000000000, False) # FIXME
81 MM.clearCoinbaseTxn.assemble()
82 MM.makeCoinbaseTxn = makeCoinbaseTxn
83 MM.onBlockChange = blockChanged
84 MM.onBlockUpdate = updateBlocks
85 MM._THISISUGLY = UpstreamBitcoind
89 from binascii import b2a_hex
90 from copy import deepcopy
91 from struct import pack, unpack
93 from util import RejectedShare, dblsha, hash2int
97 if hasattr(config, 'GotWorkURI'):
98 gotwork = jsonrpc.ServiceProxy(config.GotWorkURI)
101 if hasattr(config, 'DbOptions'):
103 db = psycopg2.connect(**config.DbOptions)
105 def getBlockHeader(username):
107 (merkleRoot, merkleTree, coinbase, prevBlock, bits, rollPrevBlk) = MRD
108 timestamp = pack('<L', int(time()))
109 hdr = b'\1\0\0\0' + prevBlock + merkleRoot + timestamp + bits + b'iolE'
110 workLog.setdefault(username, {})[merkleRoot] = MRD
116 return 'Y' if b else 'N'
122 rem_host = share.get('remoteHost', '?')
123 username = share['username']
124 reason = share.get('rejectReason', None)
125 upstreamResult = share.get('upstreamResult', None)
126 solution = share['_origdata']
127 #solution = b2a_hex(solution).decode('utf8')
128 stmt = "insert into shares (rem_host, username, our_result, upstream_result, reason, solution) values (%s, %s, %s, %s, %s, decode(%s, 'hex'))"
129 params = (rem_host, username, YN(not reason), YN(upstreamResult), reason, solution)
130 dbc.execute(stmt, params)
133 def checkShare(share):
136 (prevBlock, bits) = MM.currentBlock
137 sharePrevBlock = data[4:36]
138 if sharePrevBlock != prevBlock:
139 if sharePrevBlock == MM.lastBlock[0]:
140 raise RejectedShare('stale-prevblk')
141 raise RejectedShare('bad-prevblk')
143 shareMerkleRoot = data[36:68]
145 username = share['username']
146 if username not in workLog:
147 raise RejectedShare('unknown-user')
148 MWL = workLog[username]
149 if shareMerkleRoot not in MWL:
150 raise RejectedShare('unknown-work')
151 MRD = MWL[shareMerkleRoot]
154 if data in DupeShareHACK:
155 raise RejectedShare('duplicate')
156 DupeShareHACK[data] = None
158 shareTimestamp = unpack('<L', data[68:72])[0]
159 shareTime = share['time'] = time()
160 if shareTimestamp < shareTime - 300:
161 raise RejectedShare('time-too-old')
162 if shareTimestamp > shareTime + 7200:
163 raise RejectedShare('time-too-new')
164 if data[72:76] != bits:
165 raise RejectedShare('bad-diffbits')
166 if data[:4] != b'\1\0\0\0':
167 raise RejectedShare('bad-version')
169 blkhash = dblsha(data)
170 if blkhash[28:] != b'\0\0\0\0':
171 raise RejectedShare('H-not-zero')
172 blkhashn = hash2int(blkhash)
175 logfunc = getattr(checkShare.logger, 'info' if blkhashn <= networkTarget else 'debug')
176 logfunc('BLKHASH: %64x' % (blkhashn,))
177 logfunc(' TARGET: %64x' % (networkTarget,))
180 txlist = [deepcopy(txlist[0]),] + txlist[1:]
182 t.setCoinbase(MRD[2])
185 if blkhashn <= networkTarget:
186 logfunc("Submitting upstream")
187 UpstreamBitcoind.submitBlock(data, txlist)
188 share['upstreamResult'] = True
189 MM.updateBlock(blkhash)
194 coinbaseMrkl = t.data
195 coinbaseMrkl += blkhash
196 steps = MRD[1]._steps
197 coinbaseMrkl += pack('B', len(steps) + 1)
198 coinbaseMrkl += t.txid
201 coinbaseMrkl += b"\0\0\0\0"
203 info['hash'] = b2a_hex(blkhash).decode('ascii')
204 info['header'] = b2a_hex(data).decode('ascii')
205 info['coinbaseMrkl'] = b2a_hex(coinbaseMrkl).decode('ascii')
206 gotwork.gotwork(info)
208 checkShare.logger.warning('Failed to submit gotwork')
211 checkShare.logger = logging.getLogger('checkShare')
213 def receiveShare(share):
214 # TODO: username => userid
217 except RejectedShare as rej:
218 share['rejectReason'] = str(rej)
223 def newBlockNotification(signum, frame):
224 MM.updateMerkleTree()
225 # TODO: Force RESPOND TO LONGPOLLS?
228 from signal import signal, SIGUSR1
229 signal(SIGUSR1, newBlockNotification)
232 from jsonrpcserver import JSONRPCServer
233 import interactivemode
235 if __name__ == "__main__":
236 server = JSONRPCServer(config.JSONRPCAddress)
237 if hasattr(config, 'SecretUser'):
238 server.SecretUser = config.SecretUser
239 server.aux = MM.CoinbaseAux
240 server.getBlockHeader = getBlockHeader
241 server.receiveShare = receiveShare
242 server.RaiseRedFlags = RaiseRedFlags
243 server.serve_forever()