New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The stuff to get us off coffee. #351
Conversation
Build failure due to a version of bytestring splitter? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to talk about returning Signature
in blockchain power. This isn't a great idea, imho.
nucypher/network/protocols.py
Outdated
@@ -58,7 +58,7 @@ def rpc_store(self, sender, nodeid, key, value): | |||
if header == constants.BYTESTRING_IS_URSULA_IFACE_INFO: | |||
from nucypher.characters import Ursula | |||
stranger_ursula = Ursula.from_bytes(payload, | |||
federated_only=True) # TODO: Is federated_only the right thing here? | |||
federated_only=self.sourceNode.federated_only) # TODO: Is federated_only the right thing here? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 On having this discussion at some point. Good TODO.
tests/utilities.py
Outdated
@@ -196,8 +190,7 @@ def get_nodes_via_rest(self, address, port, node_ids): | |||
return response | |||
|
|||
def put_treasure_map_on_node(self, node, map_id, map_payload): | |||
running_node = _ALL_URSULAS[node.rest_interface.port] | |||
mock_client = TestClient(running_node.rest_app) | |||
mock_client = self.__get_mock_client_by_port(node.rest_interface.port) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thoughts on making this a fixture?
- mypy_type_check: | ||
requires: | ||
- bundle_dependencies-36 | ||
# - mypy_type_check: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we just allow failure here
nucypher/crypto/powers.py
Outdated
@@ -99,7 +104,7 @@ def sign_message(self, message: bytes): | |||
signature = self.blockchain.interface.call_backend_sign(self.account, message) | |||
return signature | |||
|
|||
def verify_message(self, address: str, pubkey: bytes, message: bytes, signature: str): | |||
def verify_message(self, address: str, pubkey: bytes, message: bytes, signature: Signature): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a step in the wrong direction.
This is meant to be an interface to what the blockchain sees. Providing the eth_keys
Signature
object is only going to allow users to make mistakes with that.
This is meant to be an interface for exactly what a smart contract would most likely interact with. I'm not very keen on providing an object that we don't have control over.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Providing the eth_keys Signature object is only going to allow users to make mistakes with that.
...why? Why doesn't it also allow them to do the right thing with that?
I'm not very keen on providing an object that we don't have control over.
Wait, what? Are you saying you are just wholesale against returning objects from dependency libraries?
I don't see how it makes any sense to return the hex. We never use it that way.
For my part, I don't even really see the point of verify_message
on BlockchainPower
- I prefer to deprecate it entirely rather than make design decisions based on it.
At the end of the day, all we care about it that we have the correct public address, and we can get that by deriving it from the public key, which we can get from the signature. This is really not the same use case as general-purpose signing. The Signature
class serves us very well for our singular need.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Providing the raw signature allows them to use eth_keys
very not misuse resistant API. Potentially they could use that to verify the signature and not the BlockchainPower
and by very simple accident, would allow them to verify a signature improperly. This should not be that simple.
Wait, what? Are you saying you are just wholesale against returning objects from dependency libraries?
That's not what I said. I'm not very keen on providing an object we don't have control over in such a critical interface as this. If we wanted to make our own Signature
class that is misuse resistant, then I'm all for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Essentially, I'm saying that eth_keys
Signature
class is too low-level to be exposed here. If we can restrict what can be done on this Signature
class, then we could return that. Like we discussed in PDX, I would prefer this, but the ecosystem has yet to catch up here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see.
Well, I think that the proper pattern here is:
- Have address to verify
- Have message purporting to come from address
- Have signature against this message
- Validate signature
- Get public key from signature
- Derive address from signature
- Confirm that addresses match
That appears to be the intention of this object, and it performs this task very well. So, do you want to cast to bytes and then cast back to Signature
? I guess that's fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that would be ideal (for now). In the verify_message
method, at least.
@@ -418,7 +418,7 @@ def call_backend_sign(self, account: str, message: bytes) -> str: | |||
address = to_canonical_address(account) | |||
sig_key = provider.ethereum_tester.backend._key_lookup[address] | |||
signed_message = sig_key.sign_msg(message) | |||
return signed_message.to_hex() | |||
return signed_message |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This breaks parity with the rest of the web3 API.
So, if we're running a test, this API is called, which returns a string. In most cases, this is what we should expect when calling some node to sign something for us, not the object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is why I hate this code and want a better signing solution from web3py here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I... don't see the problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In one case (running tests), we return a Signature
. In another, we return a string.
This is not good API design for signature, imho. They should both return an object (which is problematic), or they should both return a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, I see. So w3.eth.sign
returns a string?
In any case, remember this is a [WIP] PR - I haven't even looked at the else
clause in this function yet.
I propose that, upon merging this, we freeze #286 for review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🐧 Looks good, nice changes.
|
||
def verify_message(self, address: str, pubkey: bytes, message: bytes, signature: str): | ||
def verify_message(self, address: str, pubkey: bytes, message: bytes, signature_bytes: bytes): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not that it matters much, but is it necessary to have the type indicated in the argument name as signature_bytes
rather than just naming signature
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is a perennial wonder of mine. Since we make a Signature
object in the next line, doesn't it make sense to clarify that this object is a bytes representation? I dunno.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not too strong on one or the other way though. It's not super-important here. So, I'm ok with _bytes
@@ -27,7 +25,7 @@ class NucypherTokenActor: | |||
class ActorError(Exception): | |||
pass | |||
|
|||
def __init__(self, checksum_address: str=None, token_agent: NucypherTokenAgent=None): | |||
def __init__(self, checksum_address: str = None, token_agent: NucypherTokenAgent = None): | |||
""" | |||
:param checksum_address: If not passed, we assume this is an unknown actor | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what's the deal with self.checksum_public_address
attribute below (L37). Isn't it always going to raise an AttributeError since it doesn't exist yet?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a compromise for the case (which occurs in practice, almost without exception) where this class is mixed in with Character.
…t testerchain accounts are always unlocked.
…th other classes.
…n't need to learn at all.
Fixes #294
Fixes #295