Support for customizing logfile format and SQL insert statement
[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 hash2int(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 tryErr(func, *a, **kw):
112         IE = kw.pop('IgnoredExceptions', BaseException)
113         logger = kw.pop('Logger', None)
114         emsg = kw.pop('ErrorMsg', None)
115         try:
116                 return func(*a, **kw)
117         except IE:
118                 if logger:
119                         emsg = "%s\n" % (emsg,) if emsg else ""
120                         emsg += traceback.format_exc()
121                         logger.error(emsg)
122                 return None
123
124 class RejectedShare(ValueError):
125         pass
126
127
128 import heapq
129
130 class ScheduleDict:
131         def __init__(self):
132                 self._dict = {}
133                 self._build_heap()
134         
135         def _build_heap(self):
136                 newheap = list((v[0], k, v[1]) for k, v in self._dict.items())
137                 heapq.heapify(newheap)
138                 self._heap = newheap
139         
140         def nextTime(self):
141                 while True:
142                         (t, k, o) = self._heap[0]
143                         if k in self._dict:
144                                 break
145                         heapq.heappop(self._heap)
146                 return t
147         
148         def shift(self):
149                 while True:
150                         (t, k, o) = heapq.heappop(self._heap)
151                         if k in self._dict:
152                                 break
153                 del self._dict[k]
154                 return o
155         
156         def __setitem__(self, o, t):
157                 k = id(o)
158                 self._dict[k] = (t, o)
159                 if len(self._heap) / 2 > len(self._dict):
160                         self._build_heap()
161                 else:
162                         heapq.heappush(self._heap, (t, k, o))
163         
164         def __getitem__(self, o):
165                 return self._dict[id(o)][0]
166         
167         def __delitem__(self, o):
168                 del self._dict[id(o)]
169                 if len(self._dict) < 2:
170                         self._build_heap()
171         
172         def __len__(self):
173                 return len(self._dict)