Skip to content
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

Move AragonApp to unstructured storage #376

Merged
merged 11 commits into from
Aug 22, 2018
2 changes: 1 addition & 1 deletion contracts/acl/ACL.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ contract ACL is IACL, AragonApp, ACLHelpers {
*/
function initialize(address _permissionsCreator) public onlyInit {
initialized();
require(msg.sender == address(kernel));
require(msg.sender == address(kernel()));

_createPermission(_permissionsCreator, this, CREATE_PERMISSIONS_ROLE, _permissionsCreator);
}
Expand Down
8 changes: 4 additions & 4 deletions contracts/apm/APMRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ contract APMRegistry is AragonApp, AppProxyFactory, APMRegistryConstants {
registrar.pointRootNode(this);

// Check APM has all permissions it needss
ACL acl = ACL(kernel.acl());
ACL acl = ACL(kernel().acl());
require(acl.hasPermission(this, registrar, registrar.CREATE_NAME_ROLE()));
require(acl.hasPermission(this, acl, acl.CREATE_PERMISSIONS_ROLE()));
}
Expand Down Expand Up @@ -73,7 +73,7 @@ contract APMRegistry is AragonApp, AppProxyFactory, APMRegistryConstants {
repo.newVersion(_initialSemanticVersion, _contractAddress, _contentURI);

// Give permissions to _dev
ACL acl = ACL(kernel.acl());
ACL acl = ACL(kernel().acl());
acl.revokePermission(this, repo, repo.CREATE_VERSION_ROLE());
acl.grantPermission(_dev, repo, repo.CREATE_VERSION_ROLE());
acl.setPermissionManager(_dev, repo, repo.CREATE_VERSION_ROLE());
Expand All @@ -85,7 +85,7 @@ contract APMRegistry is AragonApp, AppProxyFactory, APMRegistryConstants {

Repo repo = newClonedRepo();

ACL(kernel.acl()).createPermission(_dev, repo, repo.CREATE_VERSION_ROLE(), _dev);
ACL(kernel().acl()).createPermission(_dev, repo, repo.CREATE_VERSION_ROLE(), _dev);

// Creates [name] subdomain in the rootNode and sets registry as resolver
// This will fail if repo name already exists
Expand All @@ -97,7 +97,7 @@ contract APMRegistry is AragonApp, AppProxyFactory, APMRegistryConstants {
}

function newClonedRepo() internal returns (Repo) {
return Repo(newAppProxy(kernel, repoAppId()));
return Repo(newAppProxy(kernel(), repoAppId()));
}

function repoAppId() internal view returns (bytes32) {
Expand Down
9 changes: 5 additions & 4 deletions contracts/apps/AppProxyBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity 0.4.18;
import "./AppStorage.sol";
import "../common/DepositableDelegateProxy.sol";
import "../kernel/KernelStorage.sol";
import "../kernel/IKernel.sol";


contract AppProxyBase is AppStorage, DepositableDelegateProxy, KernelConstants {
Expand All @@ -13,14 +14,14 @@ contract AppProxyBase is AppStorage, DepositableDelegateProxy, KernelConstants {
* @param _initializePayload Payload for call to be made after setup to initialize
*/
function AppProxyBase(IKernel _kernel, bytes32 _appId, bytes _initializePayload) public {
kernel = _kernel;
appId = _appId;
setKernel(_kernel);
setAppId(_appId);

// Implicit check that kernel is actually a Kernel
// The EVM doesn't actually provide a way for us to make sure, but we can force a revert to
// occur if the kernel is set to 0x0 or a non-code address when we try to call a method on
// it.
address appCode = getAppBase(appId);
address appCode = getAppBase(_appId);

// If initialize payload is provided, it will be executed
if (_initializePayload.length > 0) {
Expand All @@ -32,6 +33,6 @@ contract AppProxyBase is AppStorage, DepositableDelegateProxy, KernelConstants {
}

function getAppBase(bytes32 _appId) internal view returns (address) {
return kernel.getApp(keccak256(APP_BASES_NAMESPACE, _appId));
return kernel().getApp(keccak256(APP_BASES_NAMESPACE, _appId));
}
}
20 changes: 17 additions & 3 deletions contracts/apps/AppProxyPinned.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
pragma solidity 0.4.18;

import "../common/UnstructuredStorage.sol";
import "./AppProxyBase.sol";


contract AppProxyPinned is AppProxyBase {
using UnstructuredStorage for bytes32;

// keccak256("aragonOS.appStorage.pinnedCode"), used by Proxy Pinned
bytes32 internal constant PINNED_CODE_POSITION = 0xdee64df20d65e53d7f51cb6ab6d921a0a6a638a91e942e1d8d02df28e31c038e;

/**
* @dev Initialize AppProxyPinned (makes it an un-upgradeable Aragon app)
* @param _kernel Reference to organization kernel for the app
Expand All @@ -14,15 +20,15 @@ contract AppProxyPinned is AppProxyBase {
AppProxyBase(_kernel, _appId, _initializePayload)
public // solium-disable-line visibility-first
{
pinnedCode = getAppBase(appId);
require(pinnedCode != address(0));
setPinnedCode(getAppBase(_appId));
require(pinnedCode() != address(0));
}

/**
* @dev ERC897, the address the proxy would delegate calls to
*/
function implementation() public view returns (address) {
return pinnedCode;
return pinnedCode();
}

/**
Expand All @@ -31,4 +37,12 @@ contract AppProxyPinned is AppProxyBase {
function proxyType() public pure returns (uint256 proxyTypeId) {
return FORWARDING;
}

function setPinnedCode(address _pinnedCode) internal {
PINNED_CODE_POSITION.setStorageAddress(_pinnedCode);
}

function pinnedCode() internal view returns (address) {
return PINNED_CODE_POSITION.getStorageAddress();
}
}
2 changes: 1 addition & 1 deletion contracts/apps/AppProxyUpgradeable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ contract AppProxyUpgradeable is AppProxyBase {
* @dev ERC897, the address the proxy would delegate calls to
*/
function implementation() public view returns (address) {
return getAppBase(appId);
return getAppBase(appId());
}

/**
Expand Down
31 changes: 25 additions & 6 deletions contracts/apps/AppStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,33 @@

pragma solidity ^0.4.18;

import "../common/UnstructuredStorage.sol";
import "../kernel/IKernel.sol";


contract AppStorage {
IKernel public kernel;
bytes32 public appId;
address internal pinnedCode; // used by Proxy Pinned
uint256 internal initializationBlock; // used by Initializable
uint256[95] private storageOffset; // forces App storage to start at after 100 slots
uint256 private offset;
using UnstructuredStorage for bytes32;

// keccak256("aragonOS.appStorage.kernel")
bytes32 internal constant KERNEL_POSITION = 0x4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b;
// keccak256("aragonOS.appStorage.appId")
bytes32 internal constant APP_ID_POSITION = 0xd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b;
// keccak256("aragonOS.appStorage.pinnedCode"), used by Proxy Pinned
bytes32 internal constant PINNED_CODE_POSITION = 0xdee64df20d65e53d7f51cb6ab6d921a0a6a638a91e942e1d8d02df28e31c038e;

function kernel() public view returns (IKernel) {
return IKernel(KERNEL_POSITION.getStorageAddress());
}

function appId() public view returns (bytes32) {
return APP_ID_POSITION.getStorageBytes32();
}

function setKernel(IKernel _kernel) internal {
KERNEL_POSITION.setStorageAddress(address(_kernel));
}

function setAppId(bytes32 _appId) internal {
APP_ID_POSITION.setStorageBytes32(_appId);
}
}
6 changes: 3 additions & 3 deletions contracts/apps/AragonApp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ contract AragonApp is AppStorage, Initializable, ACLSyntaxSugar, VaultRecoverabl
mstore(how, byteLength)
}
}
return address(kernel) == 0 || kernel.hasPermission(_sender, address(this), _role, how);
return address(kernel()) == address(0) || kernel().hasPermission(_sender, address(this), _role, how);
}

/**
Expand All @@ -49,7 +49,7 @@ contract AragonApp is AppStorage, Initializable, ACLSyntaxSugar, VaultRecoverabl
*/
function getRecoveryVault() public view returns (address) {
// Funds recovery via a vault is only available when used with a kernel
require(address(kernel) != 0);
return kernel.getRecoveryVault();
require(address(kernel()) != address(0));
return kernel().getRecoveryVault();
}
}
16 changes: 11 additions & 5 deletions contracts/common/Initializable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,38 @@

pragma solidity ^0.4.18;

import "./UnstructuredStorage.sol";
import "../apps/AppStorage.sol";
import "../common/TimeHelpers.sol";


contract Initializable is AppStorage, TimeHelpers {
contract Initializable is TimeHelpers {
using UnstructuredStorage for bytes32;

// keccak256("aragonOS.initializable.initializationBlock")
bytes32 internal constant INITIALIZATION_BLOCK_POSITION = 0xebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e;

modifier onlyInit {
require(initializationBlock == 0);
require(getInitializationBlock() == 0);
_;
}

modifier isInitialized {
require(initializationBlock > 0);
require(getInitializationBlock() > 0);
_;
}

/**
* @return Block number in which the contract was initialized
*/
function getInitializationBlock() public view returns (uint256) {
return initializationBlock;
return INITIALIZATION_BLOCK_POSITION.getStorageUint256();
}

/**
* @dev Function to be called by top level contract after initialization has finished.
*/
function initialized() internal onlyInit {
initializationBlock = getBlockNumber();
INITIALIZATION_BLOCK_POSITION.setStorageUint256(getBlockNumber());
}
}
2 changes: 1 addition & 1 deletion contracts/common/TimeHelpers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ contract TimeHelpers {
* tests.
*/
function getTimestamp() internal view returns (uint256) {
return now;
return now; // solium-disable-line security/no-block-members
}

/**
Expand Down
32 changes: 32 additions & 0 deletions contracts/common/UnstructuredStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* SPDX-License-Identitifer: MIT
*/

pragma solidity ^0.4.18;


library UnstructuredStorage {
function getStorageAddress(bytes32 position) internal view returns (address data) {
assembly { data := sload(position) }
}

function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) {
assembly { data := sload(position) }
}

function getStorageUint256(bytes32 position) internal view returns (uint256 data) {
assembly { data := sload(position) }
}

function setStorageAddress(bytes32 position, address data) internal {
assembly { sstore(position, data) }
}

function setStorageBytes32(bytes32 position, bytes32 data) internal {
assembly { sstore(position, data) }
}

function setStorageUint256(bytes32 position, uint256 data) internal {
assembly { sstore(position, data) }
}
}
10 changes: 5 additions & 5 deletions contracts/evmscript/EVMScriptRunner.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ contract EVMScriptRunner is AppStorage, EVMScriptRegistryConstants {
}

function getExecutorRegistry() internal view returns (IEVMScriptRegistry) {
address registryAddr = kernel.getApp(EVMSCRIPT_REGISTRY_APP);
address registryAddr = kernel().getApp(EVMSCRIPT_REGISTRY_APP);
return IEVMScriptRegistry(registryAddr);
}

Expand All @@ -58,10 +58,10 @@ contract EVMScriptRunner is AppStorage, EVMScriptRegistryConstants {
}

modifier protectState {
address preKernel = kernel;
bytes32 preAppId = appId;
address preKernel = address(kernel());
bytes32 preAppId = appId();
_; // exec
require(kernel == preKernel);
require(appId == preAppId);
require(address(kernel()) == preKernel);
require(appId() == preAppId);
}
}
14 changes: 14 additions & 0 deletions test/keccak_constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,18 @@ contract('Constants', accounts => {

assert.equal(await repo.CREATE_VERSION_ROLE(), await keccakConstants.CREATE_VERSION_ROLE(), "create version role doesn't match")
})

it('checks AppStorage unstructured storage constants', async () => {
const app = await getContract('AppStubStorage').new()

assert.equal(await app.getKernelPosition(), await keccakConstants.kernelPosition(), "kernelPosition doesn't match")
assert.equal(await app.getAppIdPosition(), await keccakConstants.appIdPosition(), "appIdPosition doesn't match")
assert.equal(await app.getInitializationBlockPosition(), await keccakConstants.initializationBlockPosition(), "initializationBlockPosition doesn't match")
})

it('checks AppProxyPinned unstructured storage constants', async () => {
const app = await getContract('AppStubPinnedStorage').new()

assert.equal(await app.getPinnedCodePosition(), await keccakConstants.pinnedCodePosition(), "pinnedCodePosition doesn't match")
})
})
33 changes: 33 additions & 0 deletions test/mocks/AppStubPinnedStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
pragma solidity 0.4.18;

import "../../contracts/apps/AppProxyPinned.sol";
import "../../contracts/kernel/IKernel.sol";


contract AppStubPinnedStorage is AppProxyPinned {
bytes32 constant FAKE_APP_ID = keccak256('FAKE_APP_ID');
address constant FAKE_APP_ADDR = address(1);

// Allow the mock to be created without any arguments
function AppStubPinnedStorage()
AppProxyPinned(IKernel(0), FAKE_APP_ID, new bytes(0))
public // solium-disable-line visibility-first
{}

// Overload base to return our own fake address
function getAppBase(bytes32 _appId) internal view returns (address) {
return FAKE_APP_ADDR;
}

function setPinnedCodeExt(address _pinnedCode) public {
setPinnedCode(_pinnedCode);
}

function getPinnedCodePosition() public view returns (bytes32) {
return PINNED_CODE_POSITION;
}

function pinnedCodeExt() public view returns (address) {
return pinnedCode();
}
}
30 changes: 30 additions & 0 deletions test/mocks/AppStubStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
pragma solidity 0.4.18;

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


contract AppStubStorage is AragonApp {
function initialize() onlyInit public {
initialized();
}

function setKernelExt(IKernel _kernel) public {
setKernel(_kernel);
}

function setAppIdExt(bytes32 _appId) public {
setAppId(_appId);
}

function getKernelPosition() public view returns (bytes32) {
return KERNEL_POSITION;
}

function getAppIdPosition() public view returns (bytes32) {
return APP_ID_POSITION;
}

function getInitializationBlockPosition() public view returns (bytes32) {
return INITIALIZATION_BLOCK_POSITION;
}
}
Loading