Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .solcover.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ const skipFiles = [
'lib',
'test',
'acl/ACLSyntaxSugar.sol',
'common/DepositableStorage.sol', // Used in tests that send ETH
'common/SafeERC20.sol', // solidity-coverage fails on assembly if (https://github.com/sc-forks/solidity-coverage/issues/287)
'common/UnstructuredStorage.sol' // Used in tests that send ETH
'common/DepositableStorage.sol', // Used in tests that send ETH
'common/SafeERC20.sol', // solidity-coverage fails on assembly if (https://github.com/sc-forks/solidity-coverage/issues/287)
'common/UnstructuredStorage.sol', // Used in tests that send ETH
'relayer/Relayer.sol', // solidity-coverage uses test-rpc which does not implement eth_signTypedData
'relayer/RelayedAragonApp.sol' // solidity-coverage uses test-rpc which does not implement eth_signTypedData
]

module.exports = {
Expand Down
7 changes: 4 additions & 3 deletions contracts/apps/AppStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import "../kernel/IKernel.sol";
contract AppStorage {
using UnstructuredStorage for bytes32;

/* Hardcoded constants to save gas
bytes32 internal constant KERNEL_POSITION = keccak256("aragonOS.appStorage.kernel");
bytes32 internal constant APP_ID_POSITION = keccak256("aragonOS.appStorage.appId");
/*
* Hardcoded constants to save gas
* bytes32 internal constant KERNEL_POSITION = keccak256("aragonOS.appStorage.kernel");
* bytes32 internal constant APP_ID_POSITION = keccak256("aragonOS.appStorage.appId");
*/
bytes32 internal constant KERNEL_POSITION = 0x4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b;
bytes32 internal constant APP_ID_POSITION = 0xd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b;
Expand Down
8 changes: 6 additions & 2 deletions contracts/apps/AragonApp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ contract AragonApp is AppStorage, Autopetrified, VaultRecoverable, ReentrancyGua
string private constant ERROR_AUTH_FAILED = "APP_AUTH_FAILED";

modifier auth(bytes32 _role) {
require(canPerform(msg.sender, _role, new uint256[](0)), ERROR_AUTH_FAILED);
require(canPerform(sender(), _role, new uint256[](0)), ERROR_AUTH_FAILED);
_;
}

modifier authP(bytes32 _role, uint256[] _params) {
require(canPerform(msg.sender, _role, _params), ERROR_AUTH_FAILED);
require(canPerform(sender(), _role, _params), ERROR_AUTH_FAILED);
_;
}

Expand Down Expand Up @@ -65,4 +65,8 @@ contract AragonApp is AppStorage, Autopetrified, VaultRecoverable, ReentrancyGua
// Funds recovery via a vault is only available when used with a kernel
return kernel().getRecoveryVault(); // if kernel is not set, it will revert
}

function sender() internal view returns (address) {
return msg.sender;
}
}
53 changes: 53 additions & 0 deletions contracts/common/MemoryHelpers.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
pragma solidity ^0.4.24;


library MemoryHelpers {

function append(bytes memory self, address addr) internal pure returns (bytes memory) {
// alloc required encoded data size
uint256 dataSize = self.length;
uint256 appendedDataSize = dataSize + 32;
bytes memory appendedData = new bytes(appendedDataSize);

// copy data
uint256 inputPointer;
uint256 outputPointer;
assembly {
inputPointer := add(self, 0x20)
outputPointer := add(appendedData, 0x20)
}
memcpy(outputPointer, inputPointer, dataSize);

// append address
assembly {
let signerPointer := add(add(appendedData, 0x20), dataSize)
mstore(signerPointer, addr)
}

return appendedData;
}

// From https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol
function memcpy(uint256 output, uint256 input, uint256 length) internal pure {
uint256 len = length;
uint256 dest = output;
uint256 src = input;

// Copy word-length chunks while possible
for (; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}

// Copy remaining bytes
uint256 mask = 256 ** (32 - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
}
2 changes: 2 additions & 0 deletions contracts/kernel/IKernel.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
pragma solidity ^0.4.24;

import "../acl/IACL.sol";
import "../relayer/IRelayer.sol";
import "../common/IVaultRecoverable.sol";


Expand All @@ -16,6 +17,7 @@ interface IKernelEvents {
// This should be an interface, but interfaces can't inherit yet :(
contract IKernel is IKernelEvents, IVaultRecoverable {
function acl() public view returns (IACL);
function relayer() public view returns (IRelayer);
function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool);

function setApp(bytes32 namespace, bytes32 appId, address app) public;
Expand Down
10 changes: 10 additions & 0 deletions contracts/kernel/Kernel.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "./KernelConstants.sol";
import "./KernelStorage.sol";
import "../acl/IACL.sol";
import "../acl/ACLSyntaxSugar.sol";
import "../relayer/IRelayer.sol";
import "../common/ConversionHelpers.sol";
import "../common/IsContract.sol";
import "../common/Petrifiable.sol";
Expand Down Expand Up @@ -169,6 +170,7 @@ contract Kernel is IKernel, KernelStorage, KernelAppIds, KernelNamespaceConstant
function APP_ADDR_NAMESPACE() external pure returns (bytes32) { return KERNEL_APP_ADDR_NAMESPACE; }
function KERNEL_APP_ID() external pure returns (bytes32) { return KERNEL_CORE_APP_ID; }
function DEFAULT_ACL_APP_ID() external pure returns (bytes32) { return KERNEL_DEFAULT_ACL_APP_ID; }
function DEFAULT_RELAYER_APP_ID() external pure returns (bytes32) { return KERNEL_DEFAULT_RELAYER_APP_ID; }
/* solium-enable function-order, mixedcase */

/**
Expand Down Expand Up @@ -197,6 +199,14 @@ contract Kernel is IKernel, KernelStorage, KernelAppIds, KernelNamespaceConstant
return IACL(getApp(KERNEL_APP_ADDR_NAMESPACE, KERNEL_DEFAULT_ACL_APP_ID));
}

/**
* @dev Get the installed Relayer app
* @return Relayer app
*/
function relayer() public view returns (IRelayer) {
return IRelayer(getApp(KERNEL_APP_ADDR_NAMESPACE, KERNEL_DEFAULT_RELAYER_APP_ID));
}

/**
* @dev Function called by apps to check ACL on kernel or to check permission status
* @param _who Sender of the original call
Expand Down
2 changes: 2 additions & 0 deletions contracts/kernel/KernelConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ contract KernelAppIds {
bytes32 internal constant KERNEL_CORE_APP_ID = apmNamehash("kernel");
bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = apmNamehash("acl");
bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = apmNamehash("vault");
bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = apmNamehash("relayer");
*/
bytes32 internal constant KERNEL_CORE_APP_ID = 0x3b4bf6bf3ad5000ecf0f989d5befde585c6860fea3e574a4fab4c49d1c177d9c;
bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = 0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a;
bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = 0x7e852e0fcfce6551c13800f1e7476f982525c2b5277ba14b24339c68416336d1;
bytes32 internal constant KERNEL_DEFAULT_RELAYER_APP_ID = 0x7641595d1a2007abf0fe95c31d0b7a822954acbf6fb0cbe3bd1161d9dec9e1d3;
}


Expand Down
37 changes: 37 additions & 0 deletions contracts/lib/misc/EIP712.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
pragma solidity ^0.4.24;


contract EIP712 {
string private constant DOMAIN_TYPE = "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)";
bytes32 private constant DOMAIN_TYPEHASH = keccak256(DOMAIN_TYPE);

struct Domain {
string name;
string version;
uint256 chainId;
address verifyingContract;
}

function _domainSeparator() internal view returns (bytes32) {
return _hash(Domain({
name: _domainName(),
version: _domainVersion(),
chainId: _domainChainId(),
verifyingContract: address(this)
}));
}

function _hash(Domain domain) internal pure returns (bytes32) {
return keccak256(abi.encode(
DOMAIN_TYPEHASH,
keccak256(bytes(domain.name)),
keccak256(bytes(domain.version)),
domain.chainId,
domain.verifyingContract
));
}

function _domainName() internal view returns (string);
function _domainVersion() internal view returns (string);
function _domainChainId() internal view returns (uint256);
}
69 changes: 69 additions & 0 deletions contracts/lib/sig/ECDSA.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
pragma solidity ^0.4.24;


/**
* @title Elliptic curve signature operations
* @dev Based on https://github.com/OpenZeppelin/openzeppelin-solidity/blob/v2.0.0/contracts/cryptography/ECDSA.sol
*/
library ECDSA {

/**
* @dev Recover signer address from a message by using their signature
* @param hash bytes32 message, the hash is the signed message. What is recovered is the signer address.
* @param signature bytes signature, the signature is generated using web3.eth.sign()
*/
function recover(bytes32 hash, bytes signature)
internal
pure
returns (address)
{
bytes32 r;
bytes32 s;
uint8 v;

// Check the signature length
if (signature.length != 65) {
return (address(0));
}

// Divide the signature in r, s and v variables
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solium-disable-next-line security/no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}

// Version of signature should be 27 or 28, but 0 and 1 are also possible versions
if (v < 27) {
v += 27;
}

// If the version is correct return the signer address
if (v != 27 && v != 28) {
return (address(0));
} else {
// solium-disable-next-line arg-overflow
return ecrecover(hash, v, r, s);
}
}

/**
* toEthSignedMessageHash
* @dev prefix a bytes32 value with "\x19Ethereum Signed Message:"
* and hash the result
*/
function toEthSignedMessageHash(bytes32 hash)
internal
pure
returns (bytes32)
{
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)
);
}
}
6 changes: 6 additions & 0 deletions contracts/relayer/IRelayer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pragma solidity ^0.4.24;


contract IRelayer {
function relay(address from, address to, uint256 nonce, bytes data, uint256 gasRefund, uint256 gasPrice, bytes signature) external;
}
34 changes: 34 additions & 0 deletions contracts/relayer/RelayedAragonApp.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
pragma solidity ^0.4.24;

import "./IRelayer.sol";
import "../apps/AragonApp.sol";


contract RelayedAragonApp is AragonApp {

function sender() internal view returns (address) {
address relayer = address(_relayer());
if (msg.sender != relayer) {
return msg.sender;
}

address signer = _decodeSigner();
return signer != address(0) ? signer : relayer;
}

function _decodeSigner() internal pure returns (address signer) {
// Note that calldatasize includes one word more than the original calldata array, due to the address of the
// signer that is being appended at the end of it. Thus, we are loading the last word of the calldata array to
// fetch the actual signed of the relayed call
assembly {
let ptr := mload(0x40)
mstore(0x40, add(ptr, 0x20))
calldatacopy(ptr, sub(calldatasize, 0x20), 0x20)
signer := mload(ptr)
}
}

function _relayer() internal view returns (IRelayer) {
return kernel().relayer();
}
}
Loading