Merge branch 'master' into coinbase_height
[bitcoin:eloipool.git] / bitcoin / txn.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 .varlen import varlenDecode, varlenEncode
18 from util import dblsha
19 from struct import pack, unpack
20
21 _nullprev = b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
22
23 class Txn:
24         def __init__(self, data=None):
25                 if data:
26                         self.data = data
27                         self.idhash()
28         
29         @classmethod
30         def new(cls):
31                 o = cls()
32                 o.version = 1
33                 o.inputs = []
34                 o.outputs = []
35                 o.locktime = 0
36                 return o
37         
38         def setCoinbase(self, sigScript, seqno = 0xffffffff, height = None):
39                 if not height is None:
40                         sigScript = pack('<BL', 4, height) + sigScript
41                 self.inputs = ( ((_nullprev, 0xffffffff), sigScript, seqno), )
42         
43         def addInput(self, prevout, sigScript, seqno = 0xffffffff):
44                 self.inputs.append( (prevout, sigScript, seqno) )
45         
46         def addOutput(self, amount, pkScript):
47                 self.outputs.append( (amount, pkScript) )
48         
49         def disassemble(self, retExtra = False):
50                 self.version = unpack('<L', self.data[:4])[0]
51                 rc = [4]
52                 
53                 (inputCount, data) = varlenDecode(self.data[4:], rc)
54                 inputs = []
55                 for i in range(inputCount):
56                         prevout = (data[:32], unpack('<L', data[32:36])[0])
57                         rc[0] += 36
58                         (sigScriptLen, data) = varlenDecode(data[36:], rc)
59                         sigScript = data[:sigScriptLen]
60                         seqno = unpack('<L', data[sigScriptLen:sigScriptLen + 4])[0]
61                         data = data[sigScriptLen + 4:]
62                         rc[0] += sigScriptLen + 4
63                         inputs.append( (prevout, sigScript, seqno) )
64                 self.inputs = inputs
65                 
66                 (outputCount, data) = varlenDecode(data, rc)
67                 outputs = []
68                 for i in range(outputCount):
69                         amount = unpack('<Q', data[:8])[0]
70                         rc[0] += 8
71                         (pkScriptLen, data) = varlenDecode(data[8:], rc)
72                         pkScript = data[:pkScriptLen]
73                         data = data[pkScriptLen:]
74                         rc[0] += pkScriptLen
75                         outputs.append( (amount, pkScript) )
76                 self.outputs = outputs
77                 
78                 self.locktime = unpack('<L', data[:4])[0]
79                 if not retExtra:
80                         assert len(data) == 4
81                 else:
82                         assert data == self.data[rc[0]:]
83                         data = data[4:]
84                         rc[0] += 4
85                         self.data = self.data[:rc[0]]
86                         return data
87         
88         def isCoinbase(self):
89                 return len(self.inputs) == 1 and self.inputs[0][0] == (_nullprev, 0xffffffff)
90         
91         def getCoinbase(self):
92                 return self.inputs[0][1]
93         
94         def assemble(self):
95                 data = pack('<L', self.version)
96                 
97                 inputs = self.inputs
98                 data += varlenEncode(len(inputs))
99                 for prevout, sigScript, seqno in inputs:
100                         data += prevout[0] + pack('<L', prevout[1])
101                         data += varlenEncode(len(sigScript)) + sigScript
102                         data += pack('<L', seqno)
103                 
104                 outputs = self.outputs
105                 data += varlenEncode(len(outputs))
106                 for amount, pkScript in outputs:
107                         data += pack('<Q', amount)
108                         data += varlenEncode(len(pkScript)) + pkScript
109                 
110                 data += pack('<L', self.locktime)
111                 
112                 self.data = data
113                 self.idhash()
114         
115         def idhash(self):
116                 self.txid = dblsha(self.data)
117
118 # Txn tests
119 def _test():
120         d = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00'
121         t = Txn(d)
122         assert t.txid == b"C\xeczW\x9fUa\xa4*~\x967\xadAVg'5\xa6X\xbe'R\x18\x18\x01\xf7#\xba3\x16\xd2"
123         t.disassemble()
124         t.assemble()
125         assert t.data == d
126         assert not t.isCoinbase()
127         t = Txn.new()
128         t.addInput((b' '*32, 0), b'INPUT')
129         t.addOutput(0x10000, b'OUTPUT')
130         t.assemble()
131         assert t.txid == b'>`\x97\xecu\x8e\xb5\xef\x19k\x17d\x96sw\xb1\xf1\x9bO\x1c6\xa0\xbe\xf7N\xed\x13j\xfdHF\x1a'
132         t.disassemble()
133         t.assemble()
134         assert t.txid == b'>`\x97\xecu\x8e\xb5\xef\x19k\x17d\x96sw\xb1\xf1\x9bO\x1c6\xa0\xbe\xf7N\xed\x13j\xfdHF\x1a'
135         assert not t.isCoinbase()
136         t = Txn.new()
137         t.setCoinbase(b'COINBASE')
138         t.addOutput(0x10000, b'OUTPUT')
139         assert t.isCoinbase()
140         assert t.getCoinbase() == b'COINBASE'
141         t.assemble()
142         assert t.txid == b'n\xb9\xdc\xef\xe9\xdb(R\x8dC~-\xef~\x88d\x15+X\x13&\xb7\xbc$\xb1h\xf3g=\x9b~V'
143
144 _test()