-
Notifications
You must be signed in to change notification settings - Fork 8
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
updated contract to use a single signer' #51
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
pragma solidity 0.6.2; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "@openzeppelin/contracts/cryptography/ECDSA.sol"; | ||
import "@openzeppelin/contracts/utils/Create2.sol"; | ||
import "./ReplayProtection.sol"; | ||
import "./CallTypes.sol"; | ||
|
@@ -25,11 +26,27 @@ contract RelayHub is ReplayProtection, CallTypes, RevertMessage { | |
bool revertOnFail; | ||
} | ||
|
||
/** | ||
* Compute the signed message and fetch the signer's address. | ||
* @param _encodedCallData Encoded call data | ||
* @param _replayProtection Encoded Replay Protection | ||
* @param _replayProtectionAuthority Identify the Replay protection, default is address(0) | ||
* @param _signature Signature from signer | ||
*/ | ||
function getSigner(bytes memory _encodedCallData, bytes memory _replayProtection, address _replayProtectionAuthority, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned in the chat, I think we should use this everywhere |
||
bytes memory _signature) public view returns(address) { | ||
bytes memory encodedTxData = abi.encode(_encodedCallData, _replayProtection, _replayProtectionAuthority, address(this), getChainID()); | ||
bytes32 txid = keccak256(encodedTxData); | ||
address signer = ECDSA.recover(ECDSA.toEthSignedMessageHash(txid), _signature); | ||
return signer; | ||
} | ||
|
||
/** | ||
* Each signer has a contract account (signers address => contract address). | ||
* We check the signer has authorised the target contract and function call. Then, we pass it to the | ||
* signer's contract account to perform the final execution (to help us bypass msg.sender problem). | ||
* @param _metaTx A single meta-transaction that includes to, value and data | ||
* @param _replayProtection Encoded Replay Protection | ||
* @param _replayProtectionAuthority Identify the Replay protection, default is address(0) | ||
* @param _signer Signer's address | ||
* @param _signature Signature from signer | ||
|
@@ -41,11 +58,13 @@ contract RelayHub is ReplayProtection, CallTypes, RevertMessage { | |
address _signer, | ||
bytes memory _signature) public returns(bool, bytes memory){ | ||
|
||
bytes memory encodedData = abi.encode(CallType.CALL, _metaTx.to, _metaTx.data); | ||
// Verify the signature | ||
bytes memory encodedCallData = abi.encode(CallType.CALL, _metaTx.to, _metaTx.data); | ||
|
||
// // Reverts if fails. | ||
require(_signer == verify(encodedData, _replayProtection, _replayProtectionAuthority, _signature), | ||
// Reverts if fails. | ||
require(_signer == getSigner(encodedCallData, _replayProtection, _replayProtectionAuthority, _signature), | ||
"Signer did not sign this meta-transaction."); | ||
replayProtection(_signer, _replayProtection, _replayProtectionAuthority); | ||
|
||
// Does not revert. Lets us save the replay protection if it fails. | ||
(bool success, bytes memory returnData) = _metaTx.to.call(abi.encodePacked(_metaTx.data, _signer)); | ||
|
@@ -71,10 +90,11 @@ contract RelayHub is ReplayProtection, CallTypes, RevertMessage { | |
address _replayProtectionAuthority, | ||
address _signer, | ||
bytes memory _signature) public { | ||
bytes memory encodedData = abi.encode(CallType.BATCH, _metaTxList); | ||
bytes memory encodedCallData = abi.encode(CallType.BATCH, _metaTxList); | ||
|
||
// Reverts if fails. | ||
require(_signer == verify(encodedData, _replayProtection, _replayProtectionAuthority, _signature), "Owner did not sign this meta-transaction."); | ||
require(_signer == getSigner(encodedCallData, _replayProtection, _replayProtectionAuthority, _signature), | ||
"Owner did not sign this meta-transaction."); | ||
|
||
// Go through each revertable meta transaction and/or meta-deployment. | ||
for(uint i=0; i<_metaTxList.length; i++) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,6 @@ | ||
pragma solidity 0.6.2; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "@openzeppelin/contracts/cryptography/ECDSA.sol"; | ||
import "./IReplayProtectionAuthority.sol"; | ||
|
||
contract ReplayProtection { | ||
|
@@ -28,42 +27,33 @@ contract ReplayProtection { | |
* | ||
* Why is there no signing authority? An attacker can supply an address that returns a fixed signer | ||
* so we need to restrict it to a "pre-approved" list of authorities (DAO). | ||
* @param _callData Function name and data to be called | ||
* @param _signer Address of the signer | ||
* @param _replayProtectionAuthority What replay protection will we check? | ||
* @param _replayProtection Encoded replay protection | ||
* @param _signature Signer's signature | ||
*/ | ||
function verify(bytes memory _callData, | ||
function replayProtection(address _signer, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be called checkAndUpdateReplayProtection |
||
bytes memory _replayProtection, | ||
address _replayProtectionAuthority, | ||
bytes memory _signature) internal returns(address){ | ||
|
||
// Extract signer's address. | ||
bytes memory encodedData = abi.encode(_callData, _replayProtection, _replayProtectionAuthority, address(this), getChainID()); | ||
bytes32 txid = keccak256(encodedData); | ||
address signer = ECDSA.recover(ECDSA.toEthSignedMessageHash(txid), _signature); | ||
address _replayProtectionAuthority) internal { | ||
|
||
// Check the user's replay protection. | ||
if(_replayProtectionAuthority == multiNonceAddress) { | ||
// Assumes authority returns true or false. It may also revert. | ||
require(nonce(signer, _replayProtection), "Multinonce replay protection failed"); | ||
require(nonce(_signer, _replayProtection), "Multinonce replay protection failed"); | ||
} else if (_replayProtectionAuthority == bitFlipAddress) { | ||
require(bitflip(signer, _replayProtection), "Bitflip replay protection failed"); | ||
require(bitflip(_signer, _replayProtection), "Bitflip replay protection failed"); | ||
} else { | ||
require(IReplayProtectionAuthority(_replayProtectionAuthority).updateFor(signer, _replayProtection), "Replay protection from authority failed"); | ||
// The final "else" ensures this require() is always hit and reverts if its bad. | ||
require(IReplayProtectionAuthority(_replayProtectionAuthority).updateFor(_signer, _replayProtection), "Replay protection from authority failed"); | ||
} | ||
|
||
emit ReplayProtectionInfo(_replayProtectionAuthority, _replayProtection, txid); | ||
|
||
return signer; | ||
} | ||
|
||
/** | ||
* MultiNonce replay protection. | ||
* Explained: https://github.com/PISAresearch/metamask-comp#multinonce | ||
* Allows a user to send N queues of transactions, but transactions in each queue are accepted in order. | ||
* If queue==0, then it is a single queue (e.g. NONCE replay protection) | ||
* @param _replayProtection Contains a single nonce | ||
* @param _signer Signer's address | ||
* @param _replayProtection Contains the two nonces | ||
*/ | ||
function nonce(address _signer, bytes memory _replayProtection) internal returns(bool) { | ||
uint256 queue; | ||
|
@@ -85,6 +75,8 @@ contract ReplayProtection { | |
* Bitflip Replay Protection | ||
* Explained: https://github.com/PISAresearch/metamask-comp#bitflip | ||
* Signer flips a bit for every new transaction. Each queue supports 256 bit flips. | ||
* @param _signer Signer's address | ||
* @param _replayProtection Contains the two nonces | ||
*/ | ||
function bitflip(address _signer, bytes memory _replayProtection) internal returns(bool) { | ||
(uint256 queue, uint256 bitsToFlip) = abi.decode(_replayProtection, (uint256, uint256)); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
pragma solidity 0.6.2; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "@openzeppelin/contracts/cryptography/ECDSA.sol"; | ||
|
||
/** | ||
* Authenticates a single signer | ||
*/ | ||
contract SingleSigner { | ||
|
||
address public owner; | ||
|
||
/// @dev Due to create clone, we need to use an init() method. | ||
function init(address _owner) public { | ||
require(owner == address(0), "Owner is already set"); | ||
owner = _owner; | ||
} | ||
|
||
/// @dev Authenticates the user's signature | ||
/// @param _txid Hash of meta-tx | ||
/// @param _signature Signature of hash | ||
function authenticate(bytes32 _txid, bytes memory _signature) public view { | ||
address signer = ECDSA.recover(ECDSA.toEthSignedMessageHash(_txid), _signature); | ||
require(signer == owner, "Owner of the proxy account did not authorise the tx"); | ||
} | ||
|
||
/// @dev Return owner. | ||
function getOwner() internal view returns (address) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need this funtion? Cant we just call |
||
return owner; | ||
} | ||
|
||
} |
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 comment out of date?