Skip to content
This repository was archived by the owner on May 23, 2023. It is now read-only.

Commit 11c0088

Browse files
author
Jan Xie
committed
refactor out TransactionSigner, default to EIP155 signer
1 parent 89178a3 commit 11c0088

File tree

2 files changed

+102
-78
lines changed

2 files changed

+102
-78
lines changed

ethereum/tests/test_transactions.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ def test_transaction(filename, testname, testdata):
2626

2727
blknum = int(testdata["blocknumber"])
2828
if blknum >= config.default_config["SPURIOUS_DRAGON_FORK_BLKNUM"]:
29-
tx_sedes = transactions.EIP155Transaction
29+
signer_class = transactions.EIP155TransactionSigner
3030
else:
31-
tx_sedes = transactions.Transaction
32-
tx = rlp.decode(rlpdata, tx_sedes)
31+
signer_class = transactions.TransactionSigner
32+
tx = rlp.decode(rlpdata,
33+
sedes=transactions.Transaction,
34+
signer_class=signer_class)
3335

3436
if blknum >= config.default_config["HOMESTEAD_FORK_BLKNUM"]:
3537
tx.check_low_s_homestead()

ethereum/transactions.py

Lines changed: 97 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,98 @@
2626
eip155_v_zero = eip155_v_offset + 2 * chain_id
2727
eip155_v_one = eip155_v_zero + 1
2828

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+
29121
class Transaction(rlp.Serializable):
30122

31123
"""
@@ -60,7 +152,7 @@ class Transaction(rlp.Serializable):
60152

61153
_sender = None
62154

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):
64156
self.data = None
65157

66158
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):
74166
if self.startgas < self.intrinsic_gas_used:
75167
raise InvalidTransaction("Startgas too low")
76168

169+
self.signer = signer_class(self)
170+
77171
log.debug('deserialized tx', tx=encode_hex(self.hash)[:8])
78172

79173
@property
80174
def sender(self):
81175
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()
93177
return self._sender
94178

95179
@sender.setter
@@ -101,18 +185,7 @@ def sign(self, key):
101185
102186
A potentially already existing signature would be overridden.
103187
"""
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)
116189
return self
117190

118191
@property
@@ -183,57 +256,6 @@ def check_low_s_homestead(self):
183256
if self.s > N // 2 or self.s == 0:
184257
raise InvalidTransaction("Invalid signature S value!")
185258

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-
237259

238260
UnsignedTransaction = Transaction.exclude(['v', 'r', 's'])
239261

0 commit comments

Comments
 (0)