diff --git a/test-dapp/contracts/identity/IdentityGasRelay.sol b/test-dapp/contracts/identity/IdentityGasRelay.sol index 99a80a6..5e7a7f2 100644 --- a/test-dapp/contracts/identity/IdentityGasRelay.sol +++ b/test-dapp/contracts/identity/IdentityGasRelay.sol @@ -11,63 +11,19 @@ import "../token/ERC20Token.sol"; */ contract IdentityGasRelay is Identity { - bytes4 public constant MSG_CALL_PREFIX = bytes4(keccak256("callGasRelay(address,uint256,bytes32,uint256,uint256,address)")); - bytes4 public constant MSG_DEPLOY_PREFIX = bytes4(keccak256("deployGasRelay(uint256,bytes32,uint256,uint256,address)")); - bytes4 public constant MSG_APPROVEANDCALL_PREFIX = bytes4(keccak256("approveAndCallGasRelay(address,address,uint256,bytes32,uint256,uint256)")); + bytes4 public constant MSG_CALL_PREFIX = bytes4( + keccak256("callGasRelay(address,uint256,bytes32,uint256,uint256,address)") + ); + bytes4 public constant MSG_DEPLOY_PREFIX = bytes4( + keccak256("deployGasRelay(uint256,bytes32,uint256,uint256,address)") + ); + bytes4 public constant MSG_APPROVEANDCALL_PREFIX = bytes4( + keccak256("approveAndCallGasRelay(address,address,uint256,bytes32,uint256,uint256)") + ); event ExecutedGasRelayed(bytes32 messageHash); event ContractDeployed(address deployedAddress); - /** - * @param _messageHash that is signed - * @param _requiredPurpose key purpose for this type of call - * @param _nonce current identity nonce - * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used - * @param _gasLimit minimal gasLimit required to execute this call - * @param _gasToken token being used for paying `msg.sender` - * @param _messageSignatures rsv concatenated ethereum signed message signatures required - */ - modifier executeGasRelayed ( - bytes32 _messageHash, - uint256 _requiredPurpose, - uint _nonce, - uint _gasPrice, - uint _gasLimit, - address _gasToken, - bytes _messageSignatures - ) { - //query current gas available - uint startGas = gasleft(); - - //verify transaction parameters - require(startGas >= _gasLimit); - require(_nonce == nonce); - - //verify if signatures are valid and came from correct actors; - verifySignatures( - _requiredPurpose, - _messageHash, - _messageSignatures - ); - - //increase nonce - nonce++; - - //executes transaction - _; - - //refund gas used using contract held ERC20 tokens or ETH - if (_gasPrice > 0) { - uint256 _amount = 21000 + (startGas - gasleft()); - _amount = _amount * _gasPrice; - if (_gasToken == address(0)) { - address(msg.sender).transfer(_amount); - } else { - ERC20Token(_gasToken).transfer(msg.sender, _amount); - } - } - } - constructor( bytes32[] _keys, uint256[] _purposes, @@ -120,7 +76,7 @@ contract IdentityGasRelay is Identity { //verify transaction parameters //require(startGas >= _gasLimit); // TODO: Tune this - require(_nonce == nonce); + require(_nonce == nonce, "Wrong nonce"); //verify if signatures are valid and came from correct actors; verifySignatures( @@ -185,8 +141,8 @@ contract IdentityGasRelay is Identity { uint startGas = gasleft(); //verify transaction parameters - require(startGas >= _gasLimit); - require(_nonce == nonce); + require(startGas >= _gasLimit, "Bad gas limit"); + require(_nonce == nonce, "Bad nonce"); //verify if signatures are valid and came from correct actors; verifySignatures( @@ -226,14 +182,13 @@ contract IdentityGasRelay is Identity { * (`ERC20Token(baseToken).approve(_to, _value)` + `_to.call(_data)`). * in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken` * fixes race condition in double transaction for ERC20. - * @param _baseToken token approved for `_to` + * @param _baseToken token approved for `_to` and token being used for paying `msg.sender` * @param _to destination of call * @param _value call value (in `_baseToken`) * @param _data call data * @param _nonce current identity nonce * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used * @param _gasLimit minimal gasLimit required to execute this call - * @param _gasToken token being used for paying `msg.sender` * @param _messageSignatures rsv concatenated ethereum signed message signatures required */ function approveAndCallGasRelayed( @@ -244,7 +199,6 @@ contract IdentityGasRelay is Identity { uint _nonce, uint _gasPrice, uint _gasLimit, - address _gasToken, bytes _messageSignatures ) external @@ -255,19 +209,20 @@ contract IdentityGasRelay is Identity { uint startGas = gasleft(); //verify transaction parameters - // require(startGas >= _gasLimit); // TODO: tune this - require(_nonce == nonce); + // require(startGas >= _gasLimit, "Bad gas limit"); // TODO: tune this + require(_nonce == nonce, "Bad nonce"); //verify if signatures are valid and came from correct actors; verifySignatures( ACTION_KEY, - deployGasRelayHash( + approveAndCallHash( + _baseToken, + _to, _value, keccak256(_data), _nonce, _gasPrice, - _gasLimit, - _gasToken + _gasLimit ), _messageSignatures ); @@ -275,9 +230,9 @@ contract IdentityGasRelay is Identity { //increase nonce nonce++; - require(_baseToken != address(0)); //_baseToken should be something! - require(_to != address(this)); //no management with approveAndCall - require(_to != address(0)); //need valid destination + require(_baseToken != address(0), "Bad token address"); //_baseToken should be something! + require(_to != address(this), "Unauthorized call"); //no management with approveAndCall + require(_to != address(0), "Bad destination"); //need valid destination ERC20Token(_baseToken).approve(_to, _value); success = _commitCall(_nonce, _to, 0, _data); @@ -285,11 +240,7 @@ contract IdentityGasRelay is Identity { if (_gasPrice > 0) { uint256 _amount = 21000 + (startGas - gasleft()); _amount = _amount * _gasPrice; - if (_gasToken == address(0)) { - address(msg.sender).transfer(_amount); - } else { - ERC20Token(_gasToken).transfer(msg.sender, _amount); - } + ERC20Token(_baseToken).transfer(msg.sender, _amount); } } @@ -312,7 +263,7 @@ contract IdentityGasRelay is Identity { // calculates signHash bytes32 signHash = getSignHash(_messageHash); uint _amountSignatures = _messageSignatures.length / 65; - require(_amountSignatures == purposeThreshold[_requiredKey]); + require(_amountSignatures == purposeThreshold[_requiredKey], "Too few signatures"); bytes32 _lastKey = 0; for (uint256 i = 0; i < _amountSignatures; i++) { bytes32 _currentKey = recoverKey( @@ -320,8 +271,8 @@ contract IdentityGasRelay is Identity { _messageSignatures, i ); - require(_currentKey > _lastKey); //assert keys are different - require(keyHasPurpose(_currentKey, _requiredKey)); + require(_currentKey > _lastKey, "Bad signatures order"); //assert keys are different + require(keyHasPurpose(_currentKey, _requiredKey), "Bad key"); _lastKey = _currentKey; } return true; @@ -353,19 +304,21 @@ contract IdentityGasRelay is Identity { returns (bytes32 _callGasRelayHash) { _callGasRelayHash = keccak256( - address(this), - MSG_CALL_PREFIX, - _to, - _value, - _dataHash, - _nonce, - _gasPrice, - _gasLimit, - _gasToken + abi.encodePacked( + address(this), + MSG_CALL_PREFIX, + _to, + _value, + _dataHash, + _nonce, + _gasPrice, + _gasLimit, + _gasToken + ) ); } -/** + /** * @notice get callHash * @param _value call value (ether) * @param _dataHash call data hash @@ -388,14 +341,54 @@ contract IdentityGasRelay is Identity { returns (bytes32 _callGasRelayHash) { _callGasRelayHash = keccak256( - address(this), - MSG_DEPLOY_PREFIX, - _value, - _dataHash, - _nonce, - _gasPrice, - _gasLimit, - _gasToken + abi.encodePacked( + address(this), + MSG_DEPLOY_PREFIX, + _value, + _dataHash, + _nonce, + _gasPrice, + _gasLimit, + _gasToken + ) + ); + } + + /** + * @notice get callHash + * @param _value call value (ether) + * @param _dataHash call data hash + * @param _nonce current identity nonce + * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used + * @param _gasLimit minimal gasLimit required to execute this call + * @param _gasToken token being used for paying `msg.sender` + * @return callGasRelayHash the hash to be signed by wallet + */ + function approveAndCallHash( + address _baseToken, + address _to, + uint256 _value, + bytes32 _dataHash, + uint _nonce, + uint256 _gasPrice, + uint256 _gasLimit + ) + public + view + returns (bytes32 _callGasRelayHash) + { + _callGasRelayHash = keccak256( + abi.encodePacked( + address(this), + MSG_APPROVEANDCALL_PREFIX, + _baseToken, + _to, + _value, + _dataHash, + _nonce, + _gasPrice, + _gasLimit + ) ); } @@ -426,16 +419,18 @@ contract IdentityGasRelay is Identity { returns (bytes32 _callGasRelayHash) { _callGasRelayHash = keccak256( - address(this), - MSG_APPROVEANDCALL_PREFIX, - _baseToken, - _to, - _value, - _dataHash, - _nonce, - _gasPrice, - _gasLimit, - _gasToken + abi.encodePacked( + address(this), + MSG_APPROVEANDCALL_PREFIX, + _baseToken, + _to, + _value, + _dataHash, + _nonce, + _gasPrice, + _gasLimit, + _gasToken + ) ); } @@ -445,13 +440,13 @@ contract IdentityGasRelay is Identity { * @param _messageSignature message `_signHash` signature * @param _pos which signature to read */ - function recoverKey ( + function recoverKey( bytes32 _signHash, bytes _messageSignature, uint256 _pos ) - pure public + pure returns(bytes32) { uint8 v; @@ -459,11 +454,13 @@ contract IdentityGasRelay is Identity { bytes32 s; (v,r,s) = signatureSplit(_messageSignature, _pos); return keccak256( - ecrecover( - _signHash, - v, - r, - s + abi.encodePacked( + ecrecover( + _signHash, + v, + r, + s + ) ) ); } @@ -474,8 +471,8 @@ contract IdentityGasRelay is Identity { * @param _signatures concatenated vrs signatures */ function signatureSplit(bytes _signatures, uint256 _pos) - pure internal + pure returns (uint8 v, bytes32 r, bytes32 s) { uint pos = _pos + 1; @@ -493,7 +490,7 @@ contract IdentityGasRelay is Identity { v := and(mload(add(_signatures, mul(65,pos))), 0xff) } - require(v == 27 || v == 28); + require(v == 27 || v == 28, "Bad signature"); } /** @@ -513,7 +510,7 @@ contract IdentityGasRelay is Identity { createdContract := create(_value, add(_code, 0x20), mload(_code)) failed := iszero(extcodesize(createdContract)) } - require(!failed); + //require(!failed); removed as startGas needs to be lower then inner gasLimit } } \ No newline at end of file