-
Notifications
You must be signed in to change notification settings - Fork 444
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
feat(EIP712): parsing of TypedData payload; encoding + hashing; #833
feat(EIP712): parsing of TypedData payload; encoding + hashing; #833
Conversation
…or EIP712TypedData
@JeneaVranceanu I wonder is it ready be reviewed now, or there would be some fixes added? |
…not used for EIP712
I think it makes sense to look into the old EIP712 implementation that we already had (and maybe even remove it 👀). |
I'll ping you by the end of this day with final thoughts/changes. |
@yaroslavyaroslav No more changes for this PR.
But all of that is for a different PR. This PR will have to wait for a while to get all these updates 🙂 |
Tested it with https://react-app.walletconnect.com/ and it is working just perfect. |
Started to reviewing it this Friday, considering to continue doing this today's evening. Quite tough workload I'm having now, sorry for a delay. |
…alizedError type; - updated ParsingError and AbstractKeystoreError with description; - minor refactoring; - added 2 more EIP712 tests;
@JeneaVranceanu we have problems) |
Yes, a failing test! |
case invalidJsonFile | ||
case elementTypeInvalid | ||
case elementTypeInvalid(_ desc: String? = nil) | ||
case elementNameInvalid | ||
case functionInputInvalid | ||
case functionOutputInvalid | ||
case eventInputInvalid | ||
case parameterTypeInvalid | ||
case parameterTypeNotFound | ||
case abiInvalid |
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've decided not to add (_ desc: String? = nil)
to all error types as it would trigger more changes across the library.
It's better to do that updated in a separate PR.
// FIXME: this type is wrong - The minimum number of optional fields is 5, and those are | ||
// string name the user readable name of signing domain, i.e. the name of the DApp or the protocol. | ||
// string version the current major version of the signing domain. Signatures from different versions are not compatible. | ||
// uint256 chainId the EIP-155 chain id. The user-agent should refuse signing if it does not match the currently active chain. | ||
// address verifyingContract the address of the contract that will verify the signature. The user-agent may do contract specific phishing prevention. | ||
// bytes32 salt an disambiguating salt for the protocol. This can be used as a domain separator of last resort. | ||
public struct EIP712Domain: EIP712Hashable { |
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 something to deal with later.
} | ||
guard let newNode = rootNode.derive(path: pathAppendix, derivePrivateKey: true) else { | ||
throw AbstractKeystoreError.keyDerivationError | ||
throw AbstractKeystoreError.keyDerivationError("BIP32Keystore. Failed to derive a new node. Check given parent node and path.") |
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 really need to revisit all our errors and make sure:
- We have description;
- The description makes sense to the developers who could get this error.
…nu/web3swift into feat/eip712-dynamic-parsing * 'feat/eip712-dynamic-parsing' of github.com:JeneaVranceanu/web3swift: chore: docs update chore: updated error message
guard | ||
size >= 128 && size <= 256 && size.isMultiple(of: 32), | ||
let entropy = Data.randomBytes(length: size/8) | ||
isCorrectSize, |
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.
Although it got better, it still doesn't fit with our convention of guard else
formatting.
if self.addresses?.count == 1 && account == self.addresses?.last { | ||
guard let privateKey = try? self.getKeyData(password) else { | ||
if account == addresses?.last { | ||
guard let privateKey = try? getKeyData(password) else { |
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.
Maybe we can make this bit a bit more straightforward? Like changing the if let
above to an another guard statement.
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.
Upd: oh, I see that the execution flow continues after that line:32 throw statement. Maybe we should add at least TODO: refactor me
at the very beginning of the whole file, as I believe this function should be split in to, but not this time?
} | ||
|
||
internal struct EIP712TypeArray: Codable { | ||
public let types: [String : [EIP712TypeProperty]] |
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 wonder if this could be a concrete type rather the dictionary?
} | ||
|
||
public struct EIP712TypedData { | ||
public let types: [String: [EIP712TypeProperty]] |
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.
Same question about the concrete type here.
guard let primaryType = json["primaryType"] as? String else { | ||
throw Web3Error.inputError(desc: "EIP712Parser. Top-level string field 'primaryType' missing.") | ||
} | ||
guard let domain = json["domain"] as? [String : AnyObject] else { |
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.
Maybe we should add here some at least short reasoning about why it's left here as a dictionary, rather being casted in a concrete type?
guard let data = data(using: .utf8) else { return nil } | ||
return try data.asJsonDictionary() | ||
} | ||
} |
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're still haven't added SwiftLint ruleset here, and I wonder would we ever do this, but I believe that such a set that we're having in a stash contains the rule about keeping one empty line at the very end of each file.
guard let signature = try Web3Signer.signPersonalMessage(hash, | ||
keystore: keystore, | ||
account: account, | ||
password: password ?? "", |
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 default password value necessary?
guard let signature = try Web3Signer.signPersonalMessage(hash, | ||
keystore: keystore, | ||
account: account, | ||
password: password ?? "") | ||
password: password ?? "", |
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 default password value necessary?
case .invalidJsonFile: | ||
errorMessage = ["invalidJsonFile"] | ||
case .elementTypeInvalid(let desc): | ||
errorMessage = ["elementTypeInvalid", desc] |
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 wonder if this line is a cause that we're messing here with an array of Strings rather a plain String?
var errorMessage: [String?] | ||
switch self { | ||
case .noEntropyError(let additionalDescription): | ||
errorMessage = ["Entropy error (e.g. failed to generate a random array of bytes).", additionalDescription] |
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 wonder if this line is a cause that we're messing here with an array of Strings rather a plain String? Here it looks quite more necessary than in the previous alike code snippet, yet I wonder if it's a best way in terms of code intention clarity to implement such behaviour?
Summary of Changes
Upd: this PR is focused on giving the ability to receive a JSON payload via
eth_signTypedData
RPC call, parse this payload, do some validations (though, validations can be improved for sure), encode message (amessage
is one of the fields of received JSON), hash message and according to given types (types
also part of the received JSON and can be arbitrary), and prepare a special EIP712 hash that's used for signing of the received payload.In order to parse and sign this payload you as a user must do the following:
Test Data or Screenshots
By submitting this pull request, you are confirming the following:
develop
branch.