mirror of
https://github.com/FlipsideCrypto/web3.py.git
synced 2026-02-06 10:56:47 +00:00
3.8.1 Release
This commit is contained in:
parent
6c0f2a1e81
commit
b96369834c
@ -1,3 +1,10 @@
|
||||
3.8.1
|
||||
-----
|
||||
|
||||
* Bugfix for `eth_sign` double hashing input.
|
||||
* Removed deprecated `DelegatedSigningManager`
|
||||
* Removed deprecate `PrivateKeySigningManager`
|
||||
|
||||
3.8.0
|
||||
-----
|
||||
|
||||
|
||||
2
setup.py
2
setup.py
@ -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',
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user