3.8.1 Release

This commit is contained in:
Piper Merriam 2017-05-01 08:41:18 -06:00
parent 6c0f2a1e81
commit b96369834c
5 changed files with 8 additions and 318 deletions

View File

@ -1,3 +1,10 @@
3.8.1
-----
* Bugfix for `eth_sign` double hashing input.
* Removed deprecated `DelegatedSigningManager`
* Removed deprecate `PrivateKeySigningManager`
3.8.0
-----

View File

@ -28,7 +28,7 @@ if sys.platform == 'win32':
setup(
name='web3',
version='3.8.0',
version='3.8.1',
description="""Web3.py""",
long_description=readme,
author='Piper Merriam',

View File

@ -1,79 +0,0 @@
import pytest
from eth_utils import (
denoms,
to_normalized_address,
decode_hex,
)
from web3.providers.manager import (
PrivateKeySigningManager,
)
from testrpc.client.utils import (
mk_random_privkey,
encode_address,
)
@pytest.fixture()
def account_private_key():
return mk_random_privkey()
@pytest.fixture()
def account_public_key(account_private_key):
from ethereum.utils import privtoaddr
return to_normalized_address(encode_address(privtoaddr(account_private_key)))
@pytest.fixture()
def web3_pk_signer(web3,
account_public_key,
account_private_key,
wait_for_block,
wait_for_transaction):
pk_signing_manager = PrivateKeySigningManager(web3._requestManager)
pk_signing_manager.register_private_key(account_private_key)
assert account_public_key in pk_signing_manager.keys
wait_for_block(web3)
fund_txn_hash = web3.eth.sendTransaction({
'from': web3.eth.coinbase,
'to': account_public_key,
'value': 10 * denoms.ether,
})
wait_for_transaction(web3, fund_txn_hash)
web3._requestManager = pk_signing_manager
return web3
def test_private_key_signing_manager(web3_pk_signer,
account_private_key,
account_public_key,
wait_for_transaction):
web3 = web3_pk_signer
assert account_public_key not in web3_pk_signer.eth.accounts
with pytest.raises(ValueError):
web3.eth.sendTransaction({
'from': account_public_key,
'to': web3.eth.coinbase,
'value': 1,
})
from ethereum import tester
tester.keys.append(account_private_key)
tester.accounts.append(decode_hex(account_public_key))
txn_hash = web3.eth.sendTransaction({
'from': account_public_key,
'to': web3.eth.coinbase,
'value': 1,
})
txn_receipt = wait_for_transaction(web3, txn_hash)
txn = web3.eth.getTransaction(txn_hash)
assert txn['from'] == account_public_key

View File

@ -40,8 +40,6 @@ from web3.providers.ipc import (
)
from web3.providers.manager import (
RequestManager,
DelegatedSigningManager,
PrivateKeySigningManager,
)
from web3.utils.encoding import (
@ -62,8 +60,6 @@ class Web3(object):
# Managers
RequestManager = RequestManager
DelegatedSigningManager = DelegatedSigningManager
PrivateKeySigningManager = PrivateKeySigningManager
# Iban
Iban = Iban

View File

@ -1,29 +1,12 @@
import collections
import json
import uuid
import warnings
import rlp
from eth_utils import (
decode_hex,
encode_hex,
force_text,
is_dict,
is_string,
keccak,
to_normalized_address,
)
from web3.utils.encoding import (
to_decimal,
)
from web3.utils.transactions import (
is_bitcoin_available,
Transaction,
serialize_transaction,
add_signature_to_transaction,
)
from web3.utils.compat import (
spawn,
)
@ -79,220 +62,3 @@ class RequestManager(object):
def receive_async(self, request_id, *args, **kwargs):
raise NotImplementedError("Callback pattern not implemented")
class ManagerWrapper(object):
def __init__(self, wrapped_manager):
warnings.warn(DeprecationWarning(
"ManagerWrapper has been deprecated and will be removed from"
"web3.py in subsequen releases."
))
self.wrapped_manager = wrapped_manager
@property
def provider(self):
return self.wrapped_manager.provider
@property
def pending_requests(self):
return self.wrapped_manager.pending_requests
def setProvider(self, provider):
self.wrapped_manager.provider = provider
def request_blocking(self, *args, **kwargs):
return self.wrapped_manager.request_blocking(*args, **kwargs)
def request_async(self, *args, **kwargs):
return self.wrapped_manager.request_async(*args, **kwargs)
def receive_blocking(self, *args, **kwargs):
return self.wrapped_manager.receive_blocking(*args, **kwargs)
def receive_async(self, *args, **kwargs):
return self.wrapped_manager.receive_async(*args, **kwargs)
class BaseSendRawTransactionMixin(ManagerWrapper):
_known_transactions = None
_known_nonces = None
def __init__(self, *args, **kwargs):
self._known_transactions = collections.defaultdict(set)
self._known_nonces = collections.defaultdict(set)
super(BaseSendRawTransactionMixin, self).__init__(*args, **kwargs)
def _get_nonces_and_cleanup(self, addr, chain_nonce):
all_txns = {
txn_hash: self.request_blocking(
'eth_getTransactionByHash',
[txn_hash],
) for txn_hash in self._known_transactions[addr]
}
for txn_hash, txn in all_txns.items():
if txn is None:
continue
txn_nonce = to_decimal(txn['nonce'])
if txn_nonce < chain_nonce:
self._known_transactions[addr].discard(txn_hash)
else:
yield txn_nonce
all_known_nonces = tuple(self._known_nonces[addr])
for nonce in all_known_nonces:
if nonce < chain_nonce:
self._known_nonces[addr].discard(nonce)
else:
yield nonce
def get_chain_nonce(self, addr):
chain_nonce = to_decimal(self.request_blocking(
'eth_getTransactionCount',
[addr, 'pending']
))
return chain_nonce
def get_nonce(self, addr):
chain_nonce = self.get_chain_nonce(addr)
tracked_txn_nonces = tuple(self._get_nonces_and_cleanup(addr, chain_nonce))
nonce = max(0, chain_nonce, *tracked_txn_nonces)
if nonce == 0 and not tracked_txn_nonces:
return -1
else:
return nonce
def get_transaction_signature(self, serialized_txn):
raise NotImplementedError("Must be implemented by subclasses")
def sign_and_serialize_transaction(self, transaction):
serialized_txn = serialize_transaction(transaction)
signature = self.get_transaction_signature(transaction)
signed_transaction = add_signature_to_transaction(
serialized_txn,
signature,
)
signed_and_serialized_txn = rlp.encode(signed_transaction, Transaction)
return signed_and_serialized_txn
def construct_full_transaction(self, base_transaction):
txn_from = base_transaction['from']
full_txn = dict(**base_transaction)
full_txn.setdefault('nonce', self.get_nonce(txn_from) + 1)
full_txn.setdefault('gasPrice', self.request_blocking(
'eth_gasPrice', []
))
full_txn.setdefault('gas', hex(90000))
full_txn.setdefault('value', '0x0')
full_txn.setdefault('to', '')
full_txn.setdefault('data', '')
return full_txn
TXN_SENDING_METHODS = {
'eth_sendTransaction',
'eth_sendRawTransaction',
'personal_signAndSendTransaction',
'personal_sendTransaction',
}
def request_blocking(self, method, params):
if method == 'eth_sendTransaction':
base_transaction = params[0]
# create a fully signed transaction and send through the
# `eth_sendRawTransaction` endpoint instead.
full_transaction = self.construct_full_transaction(base_transaction)
raw_transaction_bytes = self.sign_and_serialize_transaction(
full_transaction,
)
raw_transaction_bytes_as_hex = encode_hex(raw_transaction_bytes)
return self.request_blocking(
'eth_sendRawTransaction', [raw_transaction_bytes_as_hex],
)
result = super(BaseSendRawTransactionMixin, self).request_blocking(
method, params,
)
if method in self.TXN_SENDING_METHODS:
if method == 'eth_sendRawTransaction':
txn = rlp.decode(decode_hex(params[0]), Transaction)
self._known_transactions[to_normalized_address(txn.sender)].add(result)
self._known_nonces[to_normalized_address(txn.sender)].add(txn.nonce)
else:
txn = params[0]
self._known_transactions[to_normalized_address(txn['from'])].add(result)
if 'nonce' in txn:
self._known_nonces[to_normalized_address(txn['from'])].add(
to_decimal(txn['nonce'])
)
return result
class DelegatedSigningManager(BaseSendRawTransactionMixin):
def __init__(self, *args, **kwargs):
warnings.warn(DeprecationWarning(
"DelegatedSigningManager has been deprecated and will be removed from"
"web3.py in subsequen releases."
))
self.signing_manager = kwargs.pop('signing_manager')
super(DelegatedSigningManager, self).__init__(*args, **kwargs)
def get_chain_nonce(self, addr):
signer_nonce = to_decimal(self.signing_manager.request_blocking(
'eth_getTransactionCount',
[addr, 'pending']
))
wrapped_nonce = to_decimal(self.wrapped_manager.request_blocking(
'eth_getTransactionCount',
[addr, 'pending']
))
return max(signer_nonce, wrapped_nonce)
def get_transaction_signature(self, transaction):
serialized_txn = serialize_transaction(transaction)
hash_to_sign = self.signing_manager.request_blocking(
'web3_sha3', [encode_hex(serialized_txn)],
)
signature_hex = self.signing_manager.request_blocking(
'eth_sign',
[
transaction['from'],
hash_to_sign,
],
)
signature = decode_hex(signature_hex)
return signature
class PrivateKeySigningManager(BaseSendRawTransactionMixin):
def __init__(self, *args, **kwargs):
warnings.warn(DeprecationWarning(
"PrivateKeySigningManager has been deprecated and will be removed from"
"web3.py in subsequen releases."
))
if not is_bitcoin_available():
raise ImportError(
"In order to use the `PrivateKeySigningManager` the "
"`bitcoin` and `secp256k1` packages must be installed."
)
self.keys = kwargs.pop('keys', {})
super(PrivateKeySigningManager, self).__init__(*args, **kwargs)
def register_private_key(self, key):
from bitcoin import privtopub
address = to_normalized_address(keccak(privtopub(key)[1:])[-20:])
self.keys[address] = key
def sign_and_serialize_transaction(self, transaction):
txn_from = to_normalized_address(transaction['from'])
if txn_from not in self.keys:
raise KeyError("No signing key registered for from address: {0}".format(txn_from))
transaction = Transaction(
nonce=to_decimal(transaction['nonce']),
gasprice=to_decimal(transaction['gasPrice']),
startgas=to_decimal(transaction['gas']),
to=transaction['to'],
value=to_decimal(transaction['value']),
data=decode_hex(transaction['data']),
)
transaction.sign(self.keys[txn_from])
assert to_normalized_address(transaction.sender) == txn_from
return rlp.encode(transaction, Transaction)