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

Commit 80c5cdf

Browse files
committed
Merge pull request #320 from ethereum/switch_secp
Switch to secp256k1
2 parents b6bddba + 461a88a commit 80c5cdf

File tree

5 files changed

+87
-67
lines changed

5 files changed

+87
-67
lines changed

ethereum/specials.py

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
# -*- coding: utf8 -*-
22
import bitcoin
3-
from ethereum import utils, opcodes
4-
from ethereum.utils import safe_ord, decode_hex
53
from rlp.utils import ascii_chr
4+
from secp256k1 import PublicKey, ALL_FLAGS
65

7-
try:
8-
from c_secp256k1 import ecdsa_recover_raw
9-
except ImportError:
10-
import warnings
11-
warnings.warn('missing c_secp256k1 falling back to pybitcointools')
12-
13-
from bitcoin import ecdsa_raw_recover as ecdsa_recover_raw
6+
from ethereum import utils, opcodes
7+
from ethereum.utils import safe_ord, decode_hex
148

159

1610
ZERO_PRIVKEY_ADDR = decode_hex('3f17f1962b36e491b30a40b2405849e597ba5fb5')
@@ -22,18 +16,40 @@ def proc_ecrecover(ext, msg):
2216
gas_cost = OP_GAS
2317
if msg.gas < gas_cost:
2418
return 0, 0, []
25-
b = [0] * 32
26-
msg.data.extract_copy(b, 0, 0, 32)
27-
h = b''.join([ascii_chr(x) for x in b])
19+
20+
message_hash_bytes = [0] * 32
21+
msg.data.extract_copy(message_hash_bytes, 0, 0, 32)
22+
message_hash = b''.join(map(ascii_chr, message_hash_bytes))
23+
24+
# TODO: This conversion isn't really necessary.
25+
# TODO: Invesitage if the check below is really needed.
2826
v = msg.data.extract32(32)
2927
r = msg.data.extract32(64)
3028
s = msg.data.extract32(96)
29+
3130
if r >= bitcoin.N or s >= bitcoin.N or v < 27 or v > 28:
3231
return 1, msg.gas - opcodes.GECRECOVER, []
33-
recovered_addr = ecdsa_recover_raw(h, (v, r, s))
34-
if recovered_addr in (False, (0, 0)):
32+
33+
signature_bytes = [0] * 64
34+
msg.data.extract_copy(signature_bytes, 0, 64, 32)
35+
msg.data.extract_copy(signature_bytes, 32, 96, 32)
36+
signature = b''.join(map(ascii_chr, signature_bytes))
37+
38+
pk = PublicKey(flags=ALL_FLAGS)
39+
try:
40+
pk.public_key = pk.ecdsa_recover(
41+
message_hash,
42+
pk.ecdsa_recoverable_deserialize(
43+
signature,
44+
v - 27
45+
),
46+
raw=True
47+
)
48+
except Exception:
49+
# Recovery failed
3550
return 1, msg.gas - gas_cost, []
36-
pub = bitcoin.encode_pubkey(recovered_addr, 'bin')
51+
52+
pub = pk.serialize(compressed=False)
3753
o = [0] * 12 + [safe_ord(x) for x in utils.sha3(pub[1:])[-20:]]
3854
return 1, msg.gas - gas_cost, o
3955

ethereum/transactions.py

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,17 @@
11
# -*- coding: utf8 -*-
22
import rlp
3-
from bitcoin import encode_pubkey, N, P
3+
from bitcoin import encode_pubkey, N, P, encode_privkey
44
from rlp.sedes import big_endian_int, binary
5-
from rlp.utils import decode_hex, encode_hex, str_to_bytes, ascii_chr
5+
from rlp.utils import encode_hex, str_to_bytes, ascii_chr
6+
from secp256k1 import PublicKey, ALL_FLAGS, PrivateKey
67

78
from ethereum.exceptions import InvalidTransaction
89
from ethereum import bloom
910
from ethereum import opcodes
1011
from ethereum import utils
1112
from ethereum.slogging import get_logger
12-
from ethereum.utils import TT256, mk_contract_address
13+
from ethereum.utils import TT256, mk_contract_address, zpad, int_to_32bytearray, big_endian_to_int
1314

14-
try:
15-
from c_secp256k1 import ecdsa_sign_raw, ecdsa_recover_raw
16-
except ImportError:
17-
import warnings
18-
warnings.warn('missing c_secp256k1 falling back to pybitcointools')
19-
20-
from bitcoin import ecdsa_raw_sign as ecdsa_sign_raw, ecdsa_raw_recover as ecdsa_recover_raw
2115

2216
log = get_logger('eth.chain.tx')
2317

@@ -85,10 +79,22 @@ def sender(self):
8579
log.debug('recovering sender')
8680
rlpdata = rlp.encode(self, UnsignedTransaction)
8781
rawhash = utils.sha3(rlpdata)
88-
pub = ecdsa_recover_raw(rawhash, (self.v, self.r, self.s))
89-
if pub is False:
82+
83+
pk = PublicKey(flags=ALL_FLAGS)
84+
try:
85+
pk.public_key = pk.ecdsa_recover(
86+
rawhash,
87+
pk.ecdsa_recoverable_deserialize(
88+
zpad("".join(chr(c) for c in int_to_32bytearray(self.r)), 32) + zpad("".join(chr(c) for c in int_to_32bytearray(self.s)), 32),
89+
self.v - 27
90+
),
91+
raw=True
92+
)
93+
pub = pk.serialize(compressed=False)
94+
except Exception:
9095
raise InvalidTransaction("Invalid signature values (x^3+7 is non-residue)")
91-
if pub == (0, 0):
96+
97+
if pub[1:] == "\x00" * 32:
9298
raise InvalidTransaction("Invalid signature (zero privkey cannot sign)")
9399
pub = encode_pubkey(pub, 'bin')
94100
self._sender = utils.sha3(pub[1:])[-20:]
@@ -106,10 +112,23 @@ def sign(self, key):
106112
107113
A potentially already existing signature would be overridden.
108114
"""
109-
if key in (0, '', '\x00' * 32):
115+
if key in (0, '', '\x00' * 32, '0' * 64):
110116
raise InvalidTransaction("Zero privkey cannot sign")
111117
rawhash = utils.sha3(rlp.encode(self, UnsignedTransaction))
112-
self.v, self.r, self.s = ecdsa_sign_raw(rawhash, key)
118+
119+
if len(key) == 64:
120+
# we need a binary key
121+
key = encode_privkey(key, 'bin')
122+
123+
pk = PrivateKey(key, raw=True)
124+
signature = pk.ecdsa_recoverable_serialize(
125+
pk.ecdsa_sign_recoverable(rawhash, raw=True)
126+
)
127+
signature = signature[0] + chr(signature[1])
128+
self.v = ord(signature[64]) + 27
129+
self.r = big_endian_to_int(signature[0:32])
130+
self.s = big_endian_to_int(signature[32:64])
131+
113132
self.sender = utils.privtoaddr(key)
114133
return self
115134

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ pycryptodome>=3.3.1
77
scrypt
88
rlp==0.4.4
99
https://github.com/ethereum/ethash/tarball/master
10+
git+https://github.com/ulope/secp256k1-py#egg=secp256k1

setup.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ tag = True
66
[bumpversion:file:setup.py]
77
search = version = "{current_version}"
88

9+
[aliases]
10+
test = pytest

setup.py

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,15 @@
1-
import sys
21
from setuptools import setup, find_packages
3-
from setuptools.command.test import test as TestCommand
42

53

6-
class PyTest(TestCommand):
7-
user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]
8-
9-
def initialize_options(self):
10-
TestCommand.initialize_options(self)
11-
self.pytest_args = []
12-
13-
def finalize_options(self):
14-
TestCommand.finalize_options(self)
15-
self.test_args = []
16-
self.test_suite = True
17-
18-
def run_tests(self):
19-
# import here, cause outside the eggs aren't loaded
20-
import pytest
21-
errno = pytest.main(self.pytest_args)
22-
sys.exit(errno)
23-
244
with open('README.rst') as readme_file:
255
readme = readme_file.read()
266

27-
28-
console_scripts = []
29-
30-
cmdclass = dict(test=PyTest)
31-
327
# requirements
338
install_requires = set(x.strip() for x in open('requirements.txt'))
349
install_requires_replacements = {
35-
'https://github.com/ethereum/ethash/tarball/master': 'pyethash'}
10+
'https://github.com/ethereum/ethash/tarball/master': 'pyethash',
11+
'git+https://github.com/ulope/secp256k1-py#egg=secp256k1': 'secp256k1'
12+
}
3613
install_requires = [install_requires_replacements.get(r, r) for r in install_requires]
3714

3815
# dev requirements
@@ -45,14 +22,19 @@ def run_tests(self):
4522
# see: https://github.com/ethereum/pyethapp/wiki/Development:-Versions-and-Releases
4623
version = '1.1.0'
4724

48-
setup(name="ethereum",
49-
packages=find_packages("."),
50-
description='Next generation cryptocurrency network',
51-
long_description=readme,
52-
url='https://github.com/ethereum/pyethereum/',
53-
install_requires=install_requires,
54-
tests_require=tests_require,
55-
entry_points=dict(console_scripts=console_scripts),
56-
version=version,
57-
cmdclass=cmdclass
58-
)
25+
setup(
26+
name="ethereum",
27+
packages=find_packages("."),
28+
description='Next generation cryptocurrency network',
29+
long_description=readme,
30+
url='https://github.com/ethereum/pyethereum/',
31+
install_requires=install_requires,
32+
tests_require=tests_require,
33+
setup_requires=[
34+
'pytest-runner==2.7'
35+
],
36+
dependency_links=[
37+
"https://github.com/ulope/secp256k1-py/archive/master.zip#egg=secp256k1-0.11.1"
38+
],
39+
version=version,
40+
)

0 commit comments

Comments
 (0)