-
Notifications
You must be signed in to change notification settings - Fork 13
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
Add account specifications #57
Conversation
ce7e272
to
8b7a95c
Compare
accounts/README.md
Outdated
| Type | Name | Description | | ||
|--------------|-------------|------------------------| | ||
| bytes32 | messageHash | Hash of the message | | ||
| bytes memory | messageData | EIP-712 pre-image data | |
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.
Is this always available? I'm asking because I feel like I've seen calls to this function with messageData = ""
and can imagine scenarios where this isn't available (for example, when verifying EIP-1271 signatures for example with the default verifier).
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.
So the messageData
variable has a legacy background because the older EIP-1271 spec used bytes
instead of bytes32
and the data hash. If it's not always available, I'd explicitly state that the account must not expect this argument but keep the signature consistent
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.
Looking through the checkSignatures
code, it looks like older versions of the Safe used this for the old EIP-1271 spec for contract signatures. Seeing as, since Safe v1.5.0 this is no longer the case (it uses the the new EIP-1271 signature verification for all contract signatures), could we just remove data
from the interface?
If the old checkSignatures
function is kept around for backwards compatibility on the Safe, that is fine, but I don't think it should be needed for the IAccount
interface. Essentially, you can add the following methods to the Safe so keep a backwards compatible interface, without adding the data
"tech debt" to the new IAccount
interface:
function checkSignatures(bytes32 dataHash, bytes memory /* data */, bytes memory signatures) public view {
checkSignatures(dataHash, signatures);
}
function checkNSignatures(
address executor,
bytes32 dataHash,
bytes memory /* data */,
bytes memory signatures,
uint256 requiredSignatures
) public view {
checkNSignatures(executor, dataHash, signatures, requiredSignatures);
}
This can even be implemented as a plugin to not take precious code space from the Safe
contract.
8b7a95c
to
32e5aed
Compare
32e5aed
to
e186ad2
Compare
adding a plugin, etc., for the full list, refer to [Manager's](../manager/README.md) specification). This is required because | ||
to support some functionality, such as function handlers, an Account may be required to execute a CALL | ||
operation that potentially bypasses the authorization to the Manager contract. This is required to allow the Manager to identify the transaction's sender. It is assumed | ||
that the only way to have the `msg.sender` equal to the Account address in the call frame is to execute a call through the Account's built-in authorization scheme. |
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.
Is adding msg.sender
completely covered by the reference fallback
implementation?
The reason I ask is that I would propose not having this requirement:
- If the
Account
implementation is expected to internally make configuration calls like adding a plugin, then I would change theManager.addPlugin
implementation to take an explicit caller parameter instead of having this "rule" - If the
Account
calls configuration function through its fallback handler, then this is already specified through the referencefallback
implementation right?
Sorry for being nit-picky but this paragraph "felt a bit off" to me and it's the spec 😅 which needs to be 👌.
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.
Is adding msg.sender completely covered by the reference fallback implementation?
If the account implements the specification required for the function handlers, it is covered. But it is a requirement for all state-changing methods.
It is coming as an after-effect of having function handlers. So we have the Manager set as a fallback handler, right? That means an arbitrary sender can call into the account and execute an arbitrary CALL
to the Manager. Because of this, we had to add this requirement for the Manager to authenticate the caller.
I'm also not highly fond of this requirement, but I do not see a possible workaround. I'm happy to iterate on the specs to make it more clear.
If the Account implementation is expected to internally make configuration calls like adding a plugin, then I would change the Manager.addPlugin implementation to take an explicit caller parameter instead of having this "rule"
Then the sender can specify an arbitrary caller, can't they?
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.
That means an arbitrary sender can call into the account and execute an arbitrary CALL to the Manager. Because of this, we had to add this requirement for the Manager to authenticate the caller.
I don't think this is a form of authentication in the Manager side though - a malicious account implementation or caller can just as easily add a different address at the end of the Manager call.
My point was more about questioning "why is this requirement needed". If the configuration change methods are always called via the fallback
mechanism, then this is a non-issue correct (as in, this requirement doesn't need to be mentioned as it is captured by the fallback
mechanism specification)?
Is my understanding correct that if the Account
has code where it internally makes configuration changes, then it needs to either:
IManager(this).configurationMethod()
(to ensure that it goes over the fallback mechanism, at the cost of an additionalCALL
, which I think should be fine in the name of simplicity)- Manually add the
msg.sender
at the end of the call data, in which case it likely makes sense to expose methods for configuration in the form of a library:
library ManagerLib {
function addPluginInternal(IManager manager, ...) {
bytes memory callData = abi.encodeCall(manager.addPluginInternal, (...));
manager.call(callData);
}
}
contract Account is IAccount {
using ManagerLib for IManager;
IManager manager;
function doSomeConfiguration() {
manager.addPluginInternal(...);
}
}
And in this context, this requirement is meant to capture the requirement for the second option right?
accounts/README.md
Outdated
address to, | ||
uint256 value, | ||
bytes memory data, | ||
uint8 operation |
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.
@rmeissner - do you have context on the choice of using uint8
instead of an enum
for the operation?
I think that they are equivalent from an ABI standpoint, but an enum
might be more self-documenting.
Co-authored-by: Nicholas Rodrigues Lordello <[email protected]>
accounts/README.md
Outdated
| Type | Name | Description | | ||
|--------------|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ||
| address | to | The address of the contract to be called | | ||
| uint256 | value | The amount of Native Token to be sent | |
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 wouldn't capitalise "native token" here.
Also, something that is not obvious (and related to EVM semantics) is that this value can't be specified when doing a DELEGATECALL
(since you don't send values with DELEGATECALL
and it inherits the current transaction value).
Actually, thinking about it, how do you do a DELEGATECALL
with a value under 4337? I assume it that you would have to make the Safe
call itself with a value to do the actual DELEGATECALL
you want?
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.
Also, something that is not obvious (and related to EVM semantics) is that this value can't be specified when doing a DELEGATECALL (since you don't send values with DELEGATECALL and it inherits the current transaction value).
I specified that the value would be ignored if the operation is delegatecall.
Actually, thinking about it, how do you do a DELEGATECALL with a value under 4337? I assume it that you would have to make the Safe call itself with a value to do the actual DELEGATECALL you want?
🤯 Something like this, yeah 😂
9338eef
to
1b6765c
Compare
…e-protocol-specs into specs/account-specs
1b6765c
to
2e6ebf1
Compare
This PR:
Safe{Core} Protocol
accounts #34