Skip to content

Commit

Permalink
feat: add convenience application factory for self-hosting
Browse files Browse the repository at this point in the history
  • Loading branch information
ZzzzHui committed May 10, 2024
1 parent 4072f4e commit d4c1164
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/shy-dragons-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cartesi/rollups": minor
---

Add self-hosted application factory contract
62 changes: 62 additions & 0 deletions contracts/dapp/ISelfHostedApplicationFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// (c) Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.8;

import {Authority} from "../consensus/authority/Authority.sol";
import {IAuthorityFactory} from "../consensus/authority/IAuthorityFactory.sol";
import {Application} from "./Application.sol";
import {IApplicationFactory} from "./IApplicationFactory.sol";
import {IInputBox} from "../inputs/IInputBox.sol";
import {IPortal} from "../portals/IPortal.sol";

/// @title Self-hosted Application Factory interface
interface ISelfHostedApplicationFactory {
/// @notice Get the factory used to deploy `Authority` contracts
/// @return The authority factory
function getAuthorityFactory() external view returns (IAuthorityFactory);

/// @notice Get the factory used to deploy `Application` contracts
/// @return The application factory
function getApplicationFactory()
external
view
returns (IApplicationFactory);

/// @notice Deploy new application and authority contracts deterministically.
/// @param authorityOwner The initial authority owner
/// @param inputBox The input box contract
/// @param portals The portals supported by the application
/// @param appOwner The initial Application owner
/// @param templateHash The initial machine state hash
/// @param salt The salt used to deterministically generate the addresses
/// @return The application contract
/// @return The authority contract
function deployContracts(
address authorityOwner,
IInputBox inputBox,
IPortal[] calldata portals,
address appOwner,
bytes32 templateHash,
bytes32 salt
) external returns (Application, Authority);

/// @notice Calculate the addresses of the application and authority contracts
/// to be deployed deterministically.
/// @param authorityOwner The initial authority owner
/// @param inputBox The input box contract
/// @param portals The portals supported by the application
/// @param appOwner The initial Application owner
/// @param templateHash The initial machine state hash
/// @param salt The salt used to deterministically generate the addresses
/// @return The application address
/// @return The authority address
function calculateAddresses(
address authorityOwner,
IInputBox inputBox,
IPortal[] calldata portals,
address appOwner,
bytes32 templateHash,
bytes32 salt
) external view returns (address, address);
}
92 changes: 92 additions & 0 deletions contracts/dapp/SelfHostedApplicationFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// (c) Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.8;

import {IConsensus} from "../consensus/IConsensus.sol";
import {Authority} from "../consensus/authority/Authority.sol";
import {IAuthorityFactory} from "../consensus/authority/IAuthorityFactory.sol";
import {Application} from "./Application.sol";
import {IApplicationFactory} from "./IApplicationFactory.sol";
import {ISelfHostedApplicationFactory} from "./ISelfHostedApplicationFactory.sol";
import {IInputBox} from "../inputs/IInputBox.sol";
import {IPortal} from "../portals/IPortal.sol";

/// @title Self-hosted Application Factory
/// @notice Allows anyone to reliably deploy a new Authority contract,
/// along with an Application contract already linked to it.
contract SelfHostedApplicationFactory is ISelfHostedApplicationFactory {
IAuthorityFactory immutable _authorityFactory;
IApplicationFactory immutable _applicationFactory;

/// @param authorityFactory The authority factory
/// @param applicationFactory The application factory
constructor(
IAuthorityFactory authorityFactory,
IApplicationFactory applicationFactory
) {
_authorityFactory = authorityFactory;
_applicationFactory = applicationFactory;
}

function getAuthorityFactory()
external
view
override
returns (IAuthorityFactory)
{
return _authorityFactory;
}

function getApplicationFactory()
external
view
override
returns (IApplicationFactory)
{
return _applicationFactory;
}

function deployContracts(
address authorityOwner,
IInputBox inputBox,
IPortal[] memory portals,
address appOwner,
bytes32 templateHash,
bytes32 salt
) external returns (Application application, Authority authority) {
authority = _authorityFactory.newAuthority(authorityOwner, salt);

application = _applicationFactory.newApplication(
authority,
inputBox,
portals,
appOwner,
templateHash,
salt
);
}

function calculateAddresses(
address authorityOwner,
IInputBox inputBox,
IPortal[] memory portals,
address appOwner,
bytes32 templateHash,
bytes32 salt
) external view returns (address application, address authority) {
authority = _authorityFactory.calculateAuthorityAddress(
authorityOwner,
salt
);

application = _applicationFactory.calculateApplicationAddress(
IConsensus(authority),
inputBox,
portals,
appOwner,
templateHash,
salt
);
}
}
12 changes: 10 additions & 2 deletions deploy/02_factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,16 @@ const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
log: true,
};

await deployments.deploy("AuthorityFactory", opts);
await deployments.deploy("ApplicationFactory", opts);
const AuthorityFactory = await deployments.deploy("AuthorityFactory", opts);
const ApplicationFactory = await deployments.deploy(
"ApplicationFactory",
opts,
);

await deployments.deploy("SelfHostedApplicationFactory", {
...opts,
args: [AuthorityFactory.address, ApplicationFactory.address],
});
};

export default func;
Expand Down
93 changes: 93 additions & 0 deletions test/foundry/dapp/SelfHostedApplicationFactory.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// (c) Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)

/// @title Self-hosted Application Factory Test
pragma solidity ^0.8.22;

import {IAuthorityFactory} from "contracts/consensus/authority/IAuthorityFactory.sol";
import {AuthorityFactory} from "contracts/consensus/authority/AuthorityFactory.sol";
import {Authority} from "contracts/consensus/authority/Authority.sol";
import {IApplicationFactory} from "contracts/dapp/IApplicationFactory.sol";
import {ApplicationFactory} from "contracts/dapp/ApplicationFactory.sol";
import {Application} from "contracts/dapp/Application.sol";
import {ISelfHostedApplicationFactory} from "contracts/dapp/ISelfHostedApplicationFactory.sol";
import {SelfHostedApplicationFactory} from "contracts/dapp/SelfHostedApplicationFactory.sol";
import {IInputBox} from "contracts/inputs/IInputBox.sol";
import {IPortal} from "contracts/portals/IPortal.sol";
import {TestBase} from "../util/TestBase.sol";

contract SelfHostedApplicationFactoryTest is TestBase {
IAuthorityFactory authorityFactory;
IApplicationFactory applicationFactory;
ISelfHostedApplicationFactory factory;

function setUp() external {
authorityFactory = new AuthorityFactory();
applicationFactory = new ApplicationFactory();
factory = new SelfHostedApplicationFactory(
authorityFactory,
applicationFactory
);
}

function testGetApplicationContract() external view {
assertEq(
address(factory.getApplicationFactory()),
address(applicationFactory)
);
}

function testGetAuthorityFactory() external view {
assertEq(
address(factory.getAuthorityFactory()),
address(authorityFactory)
);
}

function testDeployContracts(
address authorityOwner,
IInputBox inputBox,
IPortal[] calldata portals,
address appOwner,
bytes32 templateHash,
bytes32 salt
) external {
vm.assume(appOwner != address(0));
vm.assume(authorityOwner != address(0));

address appAddr;
address authorityAddr;

(appAddr, authorityAddr) = factory.calculateAddresses(
authorityOwner,
inputBox,
portals,
appOwner,
templateHash,
salt
);

Application application;
Authority authority;

(application, authority) = factory.deployContracts(
authorityOwner,
inputBox,
portals,
appOwner,
templateHash,
salt
);

assertEq(appAddr, address(application));
assertEq(authorityAddr, address(authority));

assertEq(authority.owner(), authorityOwner);

assertEq(address(application.getConsensus()), authorityAddr);
assertEq(address(application.getInputBox()), address(inputBox));
assertEq(abi.encode(application.getPortals()), abi.encode(portals));
assertEq(application.owner(), appOwner);
assertEq(application.getTemplateHash(), templateHash);
}
}

0 comments on commit d4c1164

Please sign in to comment.