Skip to content

Commit 6837a45

Browse files
committed
meta-txs: optimize volatile sender using calldata
1 parent a576413 commit 6837a45

12 files changed

Lines changed: 153 additions & 59 deletions

File tree

contracts/apps/AppStorage.sol

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,9 @@ contract AppStorage {
1515
* Hardcoded constants to save gas
1616
* bytes32 internal constant KERNEL_POSITION = keccak256("aragonOS.appStorage.kernel");
1717
* bytes32 internal constant APP_ID_POSITION = keccak256("aragonOS.appStorage.appId");
18-
* bytes32 internal constant VOLATILE_SENDER_POSITION = keccak256("aragonOS.appStorage.volatile.sender");
1918
*/
2019
bytes32 internal constant KERNEL_POSITION = 0x4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b;
2120
bytes32 internal constant APP_ID_POSITION = 0xd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b;
22-
bytes32 internal constant VOLATILE_SENDER_POSITION = 0xd6486d5aa3dac4242db35dd7559408452252cf8050988dbc66956eaa315379ce;
2321

2422
function kernel() public view returns (IKernel) {
2523
return IKernel(KERNEL_POSITION.getStorageAddress());
@@ -29,19 +27,11 @@ contract AppStorage {
2927
return APP_ID_POSITION.getStorageBytes32();
3028
}
3129

32-
function volatileStorageSender() public view returns (address) {
33-
return VOLATILE_SENDER_POSITION.getStorageAddress();
34-
}
35-
3630
function setKernel(IKernel _kernel) internal {
3731
KERNEL_POSITION.setStorageAddress(address(_kernel));
3832
}
3933

4034
function setAppId(bytes32 _appId) internal {
4135
APP_ID_POSITION.setStorageBytes32(_appId);
4236
}
43-
44-
function setVolatileStorageSender(address _sender) internal {
45-
VOLATILE_SENDER_POSITION.setStorageAddress(_sender);
46-
}
4737
}

contracts/common/MemoryHelpers.sol

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
pragma solidity ^0.4.24;
2+
3+
4+
library MemoryHelpers {
5+
6+
function append(bytes memory self, address addr) internal pure returns (bytes memory) {
7+
// alloc required encoded data size
8+
uint256 dataSize = self.length;
9+
uint256 appendedDataSize = dataSize + 32;
10+
bytes memory appendedData = new bytes(appendedDataSize);
11+
12+
// copy data
13+
uint256 inputPointer;
14+
uint256 outputPointer;
15+
assembly {
16+
inputPointer := add(self, 0x20)
17+
outputPointer := add(appendedData, 0x20)
18+
}
19+
memcpy(outputPointer, inputPointer, dataSize);
20+
21+
// append address
22+
assembly {
23+
let signerPointer := add(add(appendedData, 0x20), dataSize)
24+
mstore(signerPointer, addr)
25+
}
26+
27+
return appendedData;
28+
}
29+
30+
// From https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol
31+
function memcpy(uint256 dest, uint256 src, uint256 len) private pure {
32+
// Copy word-length chunks while possible
33+
for(; len >= 32; len -= 32) {
34+
assembly {
35+
mstore(dest, mload(src))
36+
}
37+
dest += 32;
38+
src += 32;
39+
}
40+
41+
// Copy remaining bytes
42+
uint256 mask = 256 ** (32 - len) - 1;
43+
assembly {
44+
let srcpart := and(mload(src), not(mask))
45+
let destpart := and(mload(dest), mask)
46+
mstore(dest, or(destpart, srcpart))
47+
}
48+
}
49+
}

contracts/kernel/IKernel.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
pragma solidity ^0.4.24;
66

77
import "../acl/IACL.sol";
8+
import "../relayer/IRelayer.sol";
89
import "../common/IVaultRecoverable.sol";
910

1011

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

2123
function setApp(bytes32 namespace, bytes32 appId, address app) public;

contracts/kernel/Kernel.sol

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import "./KernelConstants.sol";
55
import "./KernelStorage.sol";
66
import "../acl/IACL.sol";
77
import "../acl/ACLSyntaxSugar.sol";
8+
import "../relayer/IRelayer.sol";
89
import "../common/ConversionHelpers.sol";
910
import "../common/IsContract.sol";
1011
import "../common/Petrifiable.sol";
@@ -169,6 +170,7 @@ contract Kernel is IKernel, KernelStorage, KernelAppIds, KernelNamespaceConstant
169170
function APP_ADDR_NAMESPACE() external pure returns (bytes32) { return KERNEL_APP_ADDR_NAMESPACE; }
170171
function KERNEL_APP_ID() external pure returns (bytes32) { return KERNEL_CORE_APP_ID; }
171172
function DEFAULT_ACL_APP_ID() external pure returns (bytes32) { return KERNEL_DEFAULT_ACL_APP_ID; }
173+
function DEFAULT_RELAYER_APP_ID() external pure returns (bytes32) { return KERNEL_DEFAULT_RELAYER_APP_ID; }
172174
/* solium-enable function-order, mixedcase */
173175

174176
/**
@@ -197,6 +199,14 @@ contract Kernel is IKernel, KernelStorage, KernelAppIds, KernelNamespaceConstant
197199
return IACL(getApp(KERNEL_APP_ADDR_NAMESPACE, KERNEL_DEFAULT_ACL_APP_ID));
198200
}
199201

202+
/**
203+
* @dev Get the installed Relayer app
204+
* @return Relayer app
205+
*/
206+
function relayer() public view returns (IRelayer) {
207+
return IRelayer(getApp(KERNEL_APP_ADDR_NAMESPACE, KERNEL_DEFAULT_RELAYER_APP_ID));
208+
}
209+
200210
/**
201211
* @dev Function called by apps to check ACL on kernel or to check permission status
202212
* @param _who Sender of the original call

contracts/kernel/KernelConstants.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ contract KernelAppIds {
1010
bytes32 internal constant KERNEL_CORE_APP_ID = apmNamehash("kernel");
1111
bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = apmNamehash("acl");
1212
bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = apmNamehash("vault");
13+
bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = apmNamehash("relayer");
1314
*/
1415
bytes32 internal constant KERNEL_CORE_APP_ID = 0x3b4bf6bf3ad5000ecf0f989d5befde585c6860fea3e574a4fab4c49d1c177d9c;
1516
bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = 0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a;
1617
bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = 0x7e852e0fcfce6551c13800f1e7476f982525c2b5277ba14b24339c68416336d1;
18+
bytes32 internal constant KERNEL_DEFAULT_RELAYER_APP_ID = 0x7641595d1a2007abf0fe95c31d0b7a822954acbf6fb0cbe3bd1161d9dec9e1d3;
1719
}
1820

1921

contracts/relayer/IRelayer.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pragma solidity ^0.4.24;
2+
3+
4+
contract IRelayer {
5+
function relay(address from, address to, uint256 nonce, bytes calldata, bytes signature) external;
6+
}
Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,25 @@
11
pragma solidity ^0.4.24;
22

3-
3+
import "./IRelayer.sol";
44
import "../apps/AragonApp.sol";
55

66

7-
interface IRelayedAragonApp {
8-
function exec(address from, bytes calldata) external;
9-
}
7+
contract RelayedAragonApp is AragonApp {
108

11-
contract RelayedAragonApp is IRelayedAragonApp, AragonApp {
12-
bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");
9+
function sender() internal view returns (address) {
10+
address relayer = address(_relayer());
11+
if (msg.sender != relayer) return msg.sender;
1312

14-
function exec(address from, bytes calldata) external auth(RELAYER_ROLE) {
15-
setVolatileStorageSender(from);
16-
bool success = address(this).call(calldata);
17-
if (!success) revertForwardingError();
18-
setVolatileStorageSender(address(0));
13+
address signer = _decodeSigner();
14+
return signer != address(0) ? signer : relayer;
1915
}
2016

21-
function sender() internal view returns (address) {
22-
if (msg.sender != address(this)) return msg.sender;
23-
address volatileSender = volatileStorageSender();
24-
return volatileSender != address(0) ? volatileSender : address(this);
17+
function _decodeSigner() internal returns (address signer) {
18+
bytes memory calldata = msg.data;
19+
assembly { signer := mload(add(calldata, calldatasize)) }
2520
}
2621

27-
function revertForwardingError() private {
28-
assembly {
29-
let ptr := mload(0x40)
30-
returndatacopy(ptr, 0, returndatasize)
31-
revert(ptr, returndatasize)
32-
}
22+
function _relayer() internal returns (IRelayer) {
23+
return kernel().relayer();
3324
}
3425
}

contracts/relayer/Relayer.sol

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
pragma solidity ^0.4.24;
22

3+
import "./IRelayer.sol";
34
import "./RelayedAragonApp.sol";
45
import "../lib/sig/ECDSA.sol";
56
import "../apps/AragonApp.sol";
7+
import "../common/MemoryHelpers.sol";
68
import "../common/DepositableStorage.sol";
79

810

9-
contract Relayer is AragonApp, DepositableStorage {
11+
contract Relayer is IRelayer, AragonApp, DepositableStorage {
1012
using ECDSA for bytes32;
13+
using MemoryHelpers for bytes;
1114

1215
bytes32 public constant ALLOW_OFF_CHAIN_SERVICE_ROLE = keccak256("ALLOW_OFF_CHAIN_SERVICE_ROLE");
1316
bytes32 public constant DISALLOW_OFF_CHAIN_SERVICE_ROLE = keccak256("DISALLOW_OFF_CHAIN_SERVICE_ROLE");
@@ -21,7 +24,7 @@ contract Relayer is AragonApp, DepositableStorage {
2124

2225
event ServiceAllowed(address indexed service);
2326
event ServiceDisallowed(address indexed service);
24-
event TransactionRelayed(address indexed from, address indexed to, uint256 nonce, bytes calldata);
27+
event TransactionRelayed(address from, address to, uint256 nonce, bytes calldata);
2528

2629
mapping (address => bool) internal allowedServices;
2730
mapping (address => uint256) internal lastUsedNonce;
@@ -48,8 +51,9 @@ contract Relayer is AragonApp, DepositableStorage {
4851
assertValidTransaction(from, nonce, calldata, signature);
4952

5053
lastUsedNonce[from] = nonce;
51-
IRelayedAragonApp(to).exec(from, calldata);
54+
relayCall(from, to, calldata);
5255
emit TransactionRelayed(from, to, nonce, calldata);
56+
forwardReturnedData();
5357
}
5458

5559
function allowService(address service) external authP(ALLOW_OFF_CHAIN_SERVICE_ROLE, arr(service)) {
@@ -84,4 +88,24 @@ contract Relayer is AragonApp, DepositableStorage {
8488
function messageHash(bytes calldata, uint256 nonce) internal pure returns (bytes32) {
8589
return keccak256(abi.encodePacked(keccak256(calldata), nonce));
8690
}
91+
92+
function relayCall(address from, address to, bytes calldata) private {
93+
bytes memory encodedSignerCalldata = calldata.append(from);
94+
assembly {
95+
let success := call(gas, to, 0, add(encodedSignerCalldata, 0x20), mload(encodedSignerCalldata), 0, 0)
96+
switch success case 0 {
97+
let ptr := mload(0x40)
98+
returndatacopy(ptr, 0, returndatasize)
99+
revert(ptr, returndatasize)
100+
}
101+
}
102+
}
103+
104+
function forwardReturnedData() private {
105+
assembly {
106+
let ptr := mload(0x40)
107+
returndatacopy(ptr, 0, returndatasize)
108+
return(ptr, returndatasize)
109+
}
110+
}
87111
}

contracts/test/mocks/common/KeccakConstants.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ contract KeccakConstants {
2222
bytes32 public constant KERNEL_APP_ID = keccak256(abi.encodePacked(APM_NODE, keccak256("kernel")));
2323
bytes32 public constant DEFAULT_ACL_APP_ID = keccak256(abi.encodePacked(APM_NODE, keccak256("acl")));
2424
bytes32 public constant DEFAULT_VAULT_APP_ID = keccak256(abi.encodePacked(APM_NODE, keccak256("vault")));
25+
bytes32 public constant DEFAULT_RELAYER_APP_ID = keccak256(abi.encodePacked(APM_NODE, keccak256("relayer")));
2526

2627
// ACL
2728
bytes32 public constant CREATE_PERMISSIONS_ROLE = keccak256(abi.encodePacked("CREATE_PERMISSIONS_ROLE"));

contracts/test/mocks/kernel/KernelConstantsMock.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ contract KernelConstantsMock is Kernel {
1212
function getKernelAppId() external pure returns (bytes32) { return KERNEL_CORE_APP_ID; }
1313
function getDefaultACLAppId() external pure returns (bytes32) { return KERNEL_DEFAULT_ACL_APP_ID; }
1414
function getDefaultVaultAppId() external pure returns (bytes32) { return KERNEL_DEFAULT_VAULT_APP_ID; }
15+
function getDefaultRelayerAppId() external pure returns (bytes32) { return KERNEL_DEFAULT_RELAYER_APP_ID; }
1516
}

0 commit comments

Comments
 (0)