diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 96247926..08577961 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.0.4 +current_version = 1.0.5 commit = True tag = True diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 42408fd0..4b13f4ea 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -27,9 +27,10 @@ jobs: - name: Run Barge working-directory: ${{ github.workspace }}/barge env: - OPERATOR_SERVICE_VERSION: compute_envs - OPERATOR_ENGINE_VERSION: compute_envs + OPERATOR_SERVICE_VERSION: refactor_signatures CONTRACTS_VERSION: v1.0.0-alpha.28 + AQUARIUS_VERSION: refactor_signatures + PROVIDER_VERSION: refactor_signatures run: | bash -x start_ocean.sh --no-dashboard 2>&1 --with-rbac --with-provider2 --with-c2d > start_ocean.log & for i in $(seq 1 150); do @@ -47,6 +48,9 @@ jobs: coverage run --source ocean_provider -m pytest coverage report coverage xml + - name: docker logs + run: docker logs ocean_aquarius_1 && docker logs ocean_provider_1 && docker logs ocean_provider2_1 && docker logs ocean_computetodata_1 + if: ${{ failure() }} - name: Publish code coverage uses: paambaati/codeclimate-action@v2.7.5 env: diff --git a/ocean_provider/utils/accounts.py b/ocean_provider/utils/accounts.py index 9d2a271c..93d1d4b3 100644 --- a/ocean_provider/utils/accounts.py +++ b/ocean_provider/utils/accounts.py @@ -4,15 +4,14 @@ # import logging -import eth_keys -from eth_account.account import Account -from eth_account.messages import encode_defunct +from eth_keys import KeyAPI +from eth_keys.backends import NativeECCBackend from ocean_provider.exceptions import InvalidSignatureError from ocean_provider.user_nonce import get_nonce -from ocean_provider.utils.basics import get_web3 from web3 import Web3 logger = logging.getLogger(__name__) +keys = KeyAPI(NativeECCBackend) def verify_signature(signer_address, signature, original_msg, nonce): @@ -28,18 +27,37 @@ def verify_signature(signer_address, signature, original_msg, nonce): raise InvalidSignatureError(msg) message = f"{original_msg}{str(nonce)}" - address = Account.recover_message(encode_defunct(text=message), signature=signature) + signature_bytes = Web3.toBytes(hexstr=signature) + if signature_bytes[64] == 27: + new_signature = b"".join([signature_bytes[0:64], b"\x00"]) + elif signature_bytes[64] == 28: + new_signature = b"".join([signature_bytes[0:64], b"\x01"]) + else: + new_signature = signature_bytes - if address.lower() == signer_address.lower(): - return True - - msg = ( - f"Invalid signature {signature} for " - f"ethereum address {signer_address}, message {original_msg} " - f"and nonce {nonce}. Expected: {signer_address.lower()} but got {address.lower()}" + signature = keys.Signature(signature_bytes=new_signature) + message_hash = Web3.solidityKeccak( + ["bytes"], + [Web3.toBytes(text=message)], + ) + prefix = "\x19Ethereum Signed Message:\n32" + signable_hash = Web3.solidityKeccak( + ["bytes", "bytes"], [Web3.toBytes(text=prefix), Web3.toBytes(message_hash)] ) - logger.error(msg) - raise InvalidSignatureError(msg) + vkey = keys.ecdsa_recover(signable_hash, signature) + + if Web3.toChecksumAddress(signer_address) != Web3.toChecksumAddress( + vkey.to_address() + ): + msg = ( + f"Invalid signature {signature} for " + f"ethereum address {signer_address}, message {original_msg} " + f"and nonce {nonce}. Got {vkey.to_address()}" + ) + logger.error(msg) + raise InvalidSignatureError(msg) + + return True def get_private_key(wallet): @@ -47,18 +65,30 @@ def get_private_key(wallet): pk = wallet.key if not isinstance(pk, bytes): pk = Web3.toBytes(hexstr=pk) - return eth_keys.KeyAPI.PrivateKey(pk) + return keys.PrivateKey(pk) def sign_message(message, wallet): """ :param message: str :param wallet: Wallet instance - :return: `hex` value of the signed message + :return: signature """ - w3 = get_web3() - signed = w3.eth.account.sign_message( - encode_defunct(text=message), private_key=wallet.key + keys_pk = keys.PrivateKey(wallet.key) + message_hash = Web3.solidityKeccak( + ["bytes"], + [Web3.toBytes(text=message)], + ) + prefix = "\x19Ethereum Signed Message:\n32" + signable_hash = Web3.solidityKeccak( + ["bytes", "bytes"], [Web3.toBytes(text=prefix), Web3.toBytes(message_hash)] ) + signed = keys.ecdsa_sign(message_hash=signable_hash, private_key=keys_pk) + + v = str(Web3.toHex(Web3.toBytes(signed.v))) + r = str(Web3.toHex(Web3.toBytes(signed.r).rjust(32, b"\0"))) + s = str(Web3.toHex(Web3.toBytes(signed.s).rjust(32, b"\0"))) + + signature = "0x" + r[2:] + s[2:] + v[2:] - return signed.signature.hex() + return signature diff --git a/ocean_provider/utils/util.py b/ocean_provider/utils/util.py index 3f37f926..0d55bcb5 100644 --- a/ocean_provider/utils/util.py +++ b/ocean_provider/utils/util.py @@ -16,6 +16,8 @@ from jsonsempai import magic # noqa: F401 from artifacts import ERC721Template from eth_account.signers.local import LocalAccount +from eth_keys import KeyAPI +from eth_keys.backends import NativeECCBackend from flask import Response from ocean_provider.utils.accounts import sign_message from ocean_provider.utils.basics import get_config, get_provider_wallet, get_web3 @@ -26,9 +28,11 @@ from ocean_provider.utils.encryption import do_decrypt from ocean_provider.utils.services import Service from ocean_provider.utils.url import is_safe_url +from web3 import Web3 from websockets import ConnectionClosed logger = logging.getLogger(__name__) +keys = KeyAPI(NativeECCBackend) def get_metadata_url(): @@ -329,10 +333,7 @@ def sign_for_compute(wallet, owner, job_id=None): nonce = datetime.utcnow().timestamp() # prepare consumer signature on did - if job_id: - msg = f"{owner}{job_id}{nonce}" - else: - msg = f"{owner}{nonce}" + msg = f"{owner}{job_id}{nonce}" if job_id else f"{owner}{nonce}" signature = sign_message(msg, wallet) return nonce, signature diff --git a/setup.py b/setup.py index 75027b4a..86d9805c 100644 --- a/setup.py +++ b/setup.py @@ -104,7 +104,7 @@ url="https://github.com/oceanprotocol/provider-py", # fmt: off # bumpversion needs single quotes - version='1.0.4', + version='1.0.5', # fmt: on zip_safe=False, ) diff --git a/tests/test_compute.py b/tests/test_compute.py index a7a07cf4..f7cc8f31 100644 --- a/tests/test_compute.py +++ b/tests/test_compute.py @@ -242,7 +242,7 @@ def test_compute(client, publisher_wallet, consumer_wallet): tries = 0 while tries < 200: job_info = get_compute_job_info(client, compute_endpoint, payload) - if job_info["dateFinished"] and int(job_info["dateFinished"]) > 0: + if job_info["dateFinished"] and float(job_info["dateFinished"]) > 0: break tries = tries + 1 time.sleep(5)