New DelayLogForUpstream configuration option, and try submitblock(data) before getmem...
[bitcoin:eloipool.git] / util.py
1 # Eloipool - Python Bitcoin pool server
2 # Copyright (C) 2011-2012  Luke Dashjr <luke-jr+eloipool@utopios.org>
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as
6 # published by the Free Software Foundation, either version 3 of the
7 # License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 from hashlib import sha256
18 import re
19 import string
20 from struct import unpack
21 import traceback
22
23 def YN(b):
24         if b is None:
25                 return None
26         return 'Y' if b else 'N'
27
28 class shareLogFormatter:
29         _re_x = re.compile(r'^\s*(\w+)\s*(?:\(\s*(.*?)\s*\))?\s*$')
30         
31         def __init__(self, *a, **ka):
32                 self._p = self.parse(*a, **ka)
33         
34         # NOTE: This only works for psf='%s' (default)
35         def formatShare(self, *a, **ka):
36                 (stmt, params) = self.applyToShare(*a, **ka)
37                 return stmt % params
38         
39         def applyToShare(self, share):
40                 (stmt, stmtf) = self._p
41                 params = []
42                 for f in stmtf:
43                         params.append(f(share))
44                 params = tuple(params)
45                 return (stmt, params)
46         
47         @classmethod
48         def parse(self, stmt, psf = '%s'):
49                 fmt = string.Formatter()
50                 pstmt = tuple(fmt.parse(stmt))
51                 
52                 stmt = ''
53                 fmt = []
54                 for (lit, field, fmtspec, conv) in pstmt:
55                         stmt += lit
56                         if not field:
57                                 continue
58                         f = self.get_field(field)
59                         fmt.append(f)
60                         stmt += psf
61                 fmt = tuple(fmt)
62                 return (stmt, fmt)
63         
64         @classmethod
65         def get_field(self, field):
66                 m = self._re_x.match(field)
67                 if m:
68                         if m.group(2) is None:
69                                 # identifier
70                                 return lambda s: s.get(field, None)
71                         else:
72                                 # function
73                                 fn = m.group(1)
74                                 sf = self.get_field(m.group(2))
75                                 return getattr(self, 'get_field_%s' % (fn,))(sf)
76                 raise ValueError('Failed to parse field: %s' % (field,))
77         
78         @classmethod
79         def get_field_not(self, subfunc):
80                 return lambda s: not subfunc(s)
81         
82         @classmethod
83         def get_field_Q(self, subfunc):
84                 return lambda s: subfunc(s) or '?'
85         
86         @classmethod
87         def get_field_dash(self, subfunc):
88                 return lambda s: subfunc(s) or '-'
89         
90         @classmethod
91         def get_field_YN(self, subfunc):
92                 return lambda s: YN(subfunc(s))
93
94 def dblsha(b):
95         return sha256(sha256(b).digest()).digest()
96
97 def swap32(b):
98         o = b''
99         for i in range(0, len(b), 4):
100                 o += b[i + 3:i - 1 if i else None:-1]
101         return o
102
103 def Bits2Target(bits):
104         return unpack('<L', bits[:3] + b'\0')[0] * 2**(8*(bits[3] - 3))
105
106 def LEhash2int(h):
107         n = unpack('<QQQQ', h)
108         n = (n[3] << 192) | (n[2] << 128) | (n[1] << 64) | n[0]
109         return n
110
111 def BEhash2int(h):
112         n = unpack('>QQQQ', h)
113         n = (n[0] << 192) | (n[1] << 128) | (n[2] << 64) | n[3]
114         return n
115
116 def tryErr(func, *a, **kw):
117         IE = kw.pop('IgnoredExceptions', BaseException)
118         logger = kw.pop('Logger', None)
119         emsg = kw.pop('ErrorMsg', None)
120         try:
121                 return func(*a, **kw)
122         except IE:
123                 if logger:
124                         emsg = "%s\n" % (emsg,) if emsg else ""
125                         emsg += traceback.format_exc()
126                         logger.error(emsg)
127                 return None
128
129 class RejectedShare(ValueError):
130         pass
131
132 PendingUpstream = object()
133
134
135 import heapq
136
137 class ScheduleDict:
138         def __init__(self):
139                 self._dict = {}
140                 self._build_heap()
141         
142         def _build_heap(self):
143                 newheap = list((v[0], k, v[1]) for k, v in self._dict.items())
144                 heapq.heapify(newheap)
145                 self._heap = newheap
146         
147         def nextTime(self):
148                 while True:
149                         (t, k, o) = self._heap[0]
150                         if k in self._dict:
151                                 break
152                         heapq.heappop(self._heap)
153                 return t
154         
155         def shift(self):
156                 while True:
157                         (t, k, o) = heapq.heappop(self._heap)
158                         if k in self._dict:
159                                 break
160                 del self._dict[k]
161                 return o
162         
163         def __setitem__(self, o, t):
164                 k = id(o)
165                 self._dict[k] = (t, o)
166                 if len(self._heap) / 2 > len(self._dict):
167                         self._build_heap()
168                 else:
169                         heapq.heappush(self._heap, (t, k, o))
170         
171         def __getitem__(self, o):
172                 return self._dict[id(o)][0]
173         
174         def __delitem__(self, o):
175                 del self._dict[id(o)]
176                 if len(self._dict) < 2:
177                         self._build_heap()
178         
179         def __len__(self):
180                 return len(self._dict)