26
26
eip155_v_zero = eip155_v_offset + 2 * chain_id
27
27
eip155_v_one = eip155_v_zero + 1
28
28
29
+ class TransactionSigner (object ):
30
+
31
+ def __init__ (self , tx ):
32
+ if not tx :
33
+ raise ValueError ("missing transaction" )
34
+ self .tx = tx
35
+
36
+ def sign (self , key ):
37
+ if key in (0 , '' , b'\x00 ' * 32 , '0' * 64 ):
38
+ raise InvalidTransaction ("Zero privkey cannot sign" )
39
+
40
+ tx = self .tx
41
+ rawhash = utils .sha3 (rlp .encode (tx , UnsignedTransaction ))
42
+
43
+ if len (key ) == 64 :
44
+ # we need a binary key
45
+ key = encode_privkey (key , 'bin' )
46
+
47
+ v , tx .r , tx .s = ecsign (rawhash , key )
48
+ tx .v = self .encode_v (v - v_offset )
49
+
50
+ tx .sender = utils .privtoaddr (key )
51
+ return (tx .v ,tx .r ,tx .s )
52
+
53
+ def sender (self ):
54
+ tx = self .tx
55
+ if tx .v :
56
+ v = self .decode_v (tx .v )
57
+ if tx .r >= N or tx .s >= N or v > 1 or tx .r == 0 or tx .s == 0 :
58
+ raise InvalidTransaction ("Invalid signature values!" )
59
+ rawhash = utils .sha3 (self .signing_data ('verify' ))
60
+ pub = ecrecover_to_pub (rawhash , v + 27 , tx .r , tx .s )
61
+ if pub == b"\x00 " * 64 :
62
+ raise InvalidTransaction ("Invalid signature (zero privkey cannot sign)" )
63
+ return utils .sha3 (pub )[- 20 :]
64
+ else :
65
+ return 0
66
+
67
+ def encode_v (self , v ):
68
+ return v + v_zero
69
+
70
+ def decode_v (self , v ):
71
+ if not v :
72
+ return
73
+ if v in (v_zero , v_one ):
74
+ return v - v_zero
75
+ else :
76
+ raise InvalidTransaction ("invalid signature" )
77
+
78
+ def signing_data (self , mode ):
79
+ tx = self .tx
80
+ if mode == 'verify' :
81
+ if tx .v in (v_zero , v_one ):
82
+ return rlp .encode (tx , sedes = UnsignedTransaction )
83
+ elif mode == 'sign' :
84
+ return rlp .encode (tx , sedes = UnsignedTransaction )
85
+ else :
86
+ raise ValueError ("invalid mode" )
87
+
88
+
89
+ class EIP155TransactionSigner (TransactionSigner ):
90
+
91
+ def decode_chain_id (self , v ):
92
+ if v < eip155_v_offset + 2 :
93
+ raise InvalidTransaction ("invalid chain id" )
94
+ return (v - eip155_v_offset ) / 2
95
+
96
+ def decode_v (self , v ):
97
+ if not v :
98
+ return
99
+ if v in (eip155_v_zero , eip155_v_one ):
100
+ return v - eip155_v_zero
101
+ else :
102
+ return super (EIP155TransactionSigner , self ).decode_v (v )
103
+
104
+ def signing_data (self , mode ):
105
+ tx = self .tx
106
+ if mode == 'verify' :
107
+ if tx .v >= eip155_v_zero :
108
+ v = big_endian_int .serialize (self .decode_chain_id (tx .v ))
109
+ return rlp .encode (rlp .infer_sedes (tx ).serialize (tx )[:- 3 ] + [v , '' , '' ])
110
+ else :
111
+ return super (EIP155TransactionSigner , self ).signing_data (mode )
112
+ elif mode == 'sign' :
113
+ if tx .v > 0 and tx .r == 0 and tx .s == 0 :
114
+ return rlp .encode (tx , sedes = Transaction )
115
+ else :
116
+ return super (EIP155TransactionSigner , tx ).signing_data (mode )
117
+ else :
118
+ raise ValueError ("invalid mode" )
119
+
120
+
29
121
class Transaction (rlp .Serializable ):
30
122
31
123
"""
@@ -60,7 +152,7 @@ class Transaction(rlp.Serializable):
60
152
61
153
_sender = None
62
154
63
- def __init__ (self , nonce , gasprice , startgas , to , value , data , v = 0 , r = 0 , s = 0 ):
155
+ def __init__ (self , nonce , gasprice , startgas , to , value , data , v = 0 , r = 0 , s = 0 , signer_class = EIP155TransactionSigner ):
64
156
self .data = None
65
157
66
158
to = utils .normalize_address (to , allow_blank = True )
@@ -74,22 +166,14 @@ def __init__(self, nonce, gasprice, startgas, to, value, data, v=0, r=0, s=0):
74
166
if self .startgas < self .intrinsic_gas_used :
75
167
raise InvalidTransaction ("Startgas too low" )
76
168
169
+ self .signer = signer_class (self )
170
+
77
171
log .debug ('deserialized tx' , tx = encode_hex (self .hash )[:8 ])
78
172
79
173
@property
80
174
def sender (self ):
81
175
if not self ._sender :
82
- if self .v :
83
- v = self .decode_v (self .v )
84
- if self .r >= N or self .s >= N or v > 1 or self .r == 0 or self .s == 0 :
85
- raise InvalidTransaction ("Invalid signature values!" )
86
- rawhash = utils .sha3 (self .signing_data ('verify' ))
87
- pub = ecrecover_to_pub (rawhash , v + 27 , self .r , self .s )
88
- if pub == b"\x00 " * 64 :
89
- raise InvalidTransaction ("Invalid signature (zero privkey cannot sign)" )
90
- self ._sender = utils .sha3 (pub )[- 20 :]
91
- else :
92
- self ._sender = 0
176
+ self ._sender = self .signer .sender ()
93
177
return self ._sender
94
178
95
179
@sender .setter
@@ -101,18 +185,7 @@ def sign(self, key):
101
185
102
186
A potentially already existing signature would be overridden.
103
187
"""
104
- if key in (0 , '' , b'\x00 ' * 32 , '0' * 64 ):
105
- raise InvalidTransaction ("Zero privkey cannot sign" )
106
- rawhash = utils .sha3 (rlp .encode (self , UnsignedTransaction ))
107
-
108
- if len (key ) == 64 :
109
- # we need a binary key
110
- key = encode_privkey (key , 'bin' )
111
-
112
- v , self .r , self .s = ecsign (rawhash , key )
113
- self .v = self .encode_v (v - v_offset )
114
-
115
- self .sender = utils .privtoaddr (key )
188
+ self .signer .sign (key )
116
189
return self
117
190
118
191
@property
@@ -183,57 +256,6 @@ def check_low_s_homestead(self):
183
256
if self .s > N // 2 or self .s == 0 :
184
257
raise InvalidTransaction ("Invalid signature S value!" )
185
258
186
- def encode_v (self , v ):
187
- return v + v_zero
188
-
189
- def decode_v (self , v ):
190
- if not v :
191
- return
192
- if v in (v_zero , v_one ):
193
- return v - v_zero
194
- else :
195
- raise InvalidTransaction ("invalid signature" )
196
-
197
- def signing_data (self , mode ):
198
- if mode == 'verify' :
199
- if self .v in (v_zero , v_one ):
200
- return rlp .encode (self , sedes = UnsignedTransaction )
201
- elif mode == 'sign' :
202
- return rlp .encode (self , sedes = UnsignedTransaction )
203
- else :
204
- raise ValueError ("invalid mode" )
205
-
206
-
207
- class EIP155Transaction (Transaction ):
208
-
209
- def decode_chain_id (self , v ):
210
- if v < eip155_v_offset + 2 :
211
- raise InvalidTransaction ("invalid chain id" )
212
- return (v - eip155_v_offset ) / 2
213
-
214
- def decode_v (self , v ):
215
- if not v :
216
- return
217
- if v in (eip155_v_zero , eip155_v_one ):
218
- return v - eip155_v_zero
219
- else :
220
- return super (EIP155Transaction , self ).decode_v (v )
221
-
222
- def signing_data (self , mode ):
223
- if mode == 'verify' :
224
- if self .v >= eip155_v_zero :
225
- v = big_endian_int .serialize (self .decode_chain_id (self .v ))
226
- return rlp .encode (rlp .infer_sedes (self ).serialize (self )[:- 3 ] + [v , '' , '' ])
227
- else :
228
- return super (EIP155Transaction , self ).signing_data (mode )
229
- elif mode == 'sign' :
230
- if self .v > 0 and self .r == 0 and self .s == 0 :
231
- return rlp .encode (self , sedes = Transaction )
232
- else :
233
- return super (EIP155Transaction , self ).signing_data (mode )
234
- else :
235
- raise ValueError ("invalid mode" )
236
-
237
259
238
260
UnsignedTransaction = Transaction .exclude (['v' , 'r' , 's' ])
239
261
0 commit comments