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

On-chain node configuration #337

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/early-boats-remember.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cartesi/rollups": minor
---

Add data availability configuration to application contract
28 changes: 28 additions & 0 deletions contracts/common/DataAvailability.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// (c) Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)

pragma solidity ^0.8.8;

import {IInputBox} from "../inputs/IInputBox.sol";

/// @title Data Availability
/// @notice Defines the signatures of data availability solutions.
interface DataAvailability {
/// @notice The application receives inputs only from
/// a contract that implements the `IInputBox` interface.
/// @param inputBox The input box contract address
function InputBox(IInputBox inputBox) external;

/// @notice The application receives inputs from
/// a contract that implements the `IInputBox` interface,
/// and from Espresso, starting from a given block height,
/// and for a given namespace ID.
/// @param inputBox The input box contract address
/// @param fromBlock Height of first Espresso block to consider
/// @param namespaceId The Espresso namespace ID
function InputBoxAndEspresso(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure InputBoxAndEspresso should be part of a global official DataAvailability interface. I am expecting people to be able to add their own without messing with our contracts.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's good to have at least the ones we fully support defined here so that deployment front-ends and the node can utilize the same definitions. :-)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

People can still provide their own DA config blobs on deployment, and hack their own DA provider node plug-in that understands and decodes that blob.

Copy link
Collaborator Author

@guidanoli guidanoli Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if someone wants to add a new DA signature, they can just open a PR. It won't affect the bytecode of our deployed contracts, and therefore won't trigger any redeployments. Therefore, we can release minor versions with these changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I see. But "fully supporting" an integration is a complicated term, and I'm still not very comfortable with our regular contracts mentioning specific integrations/extensions, I feel it should be separate. Couldn't these signatures be placed in separate interfaces?
I'd like to hear other opinions, maybe @pedroargento or @tuler?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could totally place these definitions in a third place, but it is convenient to place them here because the rollups-node repo already uses the artifacts from rollups-contracts (which includes contracts/interface ABIs), and front-ends often depend on the @cartesi/rollups npm package to interact with our contracts (e.g. when deploying).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But yeah, we could create a new repo (e.g. rollups-da-config) with just this file, which would not be imported by rollups-contracts, but would be imported/used by the node and deployment front-ends.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not very comfortable with our regular contracts mentioning specific integrations/extensions

Our regular contracts are not mentioning integrations/extensions. If you were to draw a dependency graph of the rollups-contracts with this PR, this interface would be isolated from every other contract. It would not be referenced by any other contract, and therefore not influence any of them. That's why we could, in theory, move it to a separate repo.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be clear that I'm not advocating to moving it to somewhere else. I think it's okay to keep it here. If this interface grows out of control, we can think about create a dedicated repo for it.

IInputBox inputBox,
uint256 fromBlock,
uint32 namespaceId
) external;
}
17 changes: 16 additions & 1 deletion contracts/dapp/Application.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ contract Application is
/// @dev See the `getConsensus` and `migrateToConsensus` functions.
IConsensus internal _consensus;

/// @notice The data availability solution.
/// @dev See the `getDataAvailability` function.
bytes internal _dataAvailability;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why bytes?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, why not a ERC-165 contract?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because with bytes we can encode more data specific to that application.
For example, applications that use Espresso would specify their namespace and Espresso block height.


/// @notice Creates an `Application` contract.
/// @param consensus The initial consensus contract
/// @param initialOwner The initial application owner
Expand All @@ -49,10 +53,12 @@ contract Application is
constructor(
IConsensus consensus,
address initialOwner,
bytes32 templateHash
bytes32 templateHash,
bytes memory dataAvailability
) Ownable(initialOwner) {
_templateHash = templateHash;
_consensus = consensus;
_dataAvailability = dataAvailability;
}

/// @notice Accept Ether transfers.
Expand Down Expand Up @@ -136,6 +142,15 @@ contract Application is
return _consensus;
}

function getDataAvailability()
external
view
override
returns (bytes memory)
{
return _dataAvailability;
}

function owner() public view override(IOwnable, Ownable) returns (address) {
return super.owner();
}
Expand Down
34 changes: 28 additions & 6 deletions contracts/dapp/ApplicationFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,23 @@ contract ApplicationFactory is IApplicationFactory {
function newApplication(
IConsensus consensus,
address appOwner,
bytes32 templateHash
bytes32 templateHash,
bytes calldata dataAvailability
) external override returns (IApplication) {
IApplication appContract = new Application(
consensus,
appOwner,
templateHash
templateHash,
dataAvailability
);

emit ApplicationCreated(consensus, appOwner, templateHash, appContract);
emit ApplicationCreated(
consensus,
appOwner,
templateHash,
dataAvailability,
appContract
);

return appContract;
}
Expand All @@ -33,15 +41,23 @@ contract ApplicationFactory is IApplicationFactory {
IConsensus consensus,
address appOwner,
bytes32 templateHash,
bytes calldata dataAvailability,
bytes32 salt
) external override returns (IApplication) {
IApplication appContract = new Application{salt: salt}(
consensus,
appOwner,
templateHash
templateHash,
dataAvailability
);

emit ApplicationCreated(consensus, appOwner, templateHash, appContract);
emit ApplicationCreated(
consensus,
appOwner,
templateHash,
dataAvailability,
appContract
);

return appContract;
}
Expand All @@ -50,6 +66,7 @@ contract ApplicationFactory is IApplicationFactory {
IConsensus consensus,
address appOwner,
bytes32 templateHash,
bytes calldata dataAvailability,
bytes32 salt
) external view override returns (address) {
return
Expand All @@ -58,7 +75,12 @@ contract ApplicationFactory is IApplicationFactory {
keccak256(
abi.encodePacked(
type(Application).creationCode,
abi.encode(consensus, appOwner, templateHash)
abi.encode(
consensus,
appOwner,
templateHash,
dataAvailability
)
)
)
);
Expand Down
5 changes: 5 additions & 0 deletions contracts/dapp/IApplication.sol
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,9 @@ interface IApplication is IOwnable {
/// @notice Get the current consensus.
/// @return The current consensus
function getConsensus() external view returns (IConsensus);

/// @notice Get the data availability solution used by application.
/// @return Solidity ABI-encoded function call that describes
/// the source of inputs that should be fed to the application.
function getDataAvailability() external view returns (bytes memory);
}
6 changes: 5 additions & 1 deletion contracts/dapp/IApplicationFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface IApplicationFactory {
IConsensus indexed consensus,
address appOwner,
bytes32 templateHash,
bytes dataAvailability,
IApplication appContract
);

Expand All @@ -35,7 +36,8 @@ interface IApplicationFactory {
function newApplication(
IConsensus consensus,
address appOwner,
bytes32 templateHash
bytes32 templateHash,
bytes calldata dataAvailability
) external returns (IApplication);

/// @notice Deploy a new application deterministically.
Expand All @@ -50,6 +52,7 @@ interface IApplicationFactory {
IConsensus consensus,
address appOwner,
bytes32 templateHash,
bytes calldata dataAvailability,
bytes32 salt
) external returns (IApplication);

Expand All @@ -65,6 +68,7 @@ interface IApplicationFactory {
IConsensus consensus,
address appOwner,
bytes32 templateHash,
bytes calldata dataAvailability,
bytes32 salt
) external view returns (address);
}
2 changes: 2 additions & 0 deletions contracts/dapp/ISelfHostedApplicationFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ interface ISelfHostedApplicationFactory {
uint256 epochLength,
address appOwner,
bytes32 templateHash,
bytes calldata dataAvailability,
bytes32 salt
) external returns (IApplication, IAuthority);

Expand All @@ -54,6 +55,7 @@ interface ISelfHostedApplicationFactory {
uint256 epochLength,
address appOwner,
bytes32 templateHash,
bytes calldata dataAvailability,
bytes32 salt
) external view returns (address, address);
}
4 changes: 4 additions & 0 deletions contracts/dapp/SelfHostedApplicationFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ contract SelfHostedApplicationFactory is ISelfHostedApplicationFactory {
uint256 epochLength,
address appOwner,
bytes32 templateHash,
bytes calldata dataAvailability,
bytes32 salt
) external returns (IApplication application, IAuthority authority) {
authority = _authorityFactory.newAuthority(
Expand All @@ -62,6 +63,7 @@ contract SelfHostedApplicationFactory is ISelfHostedApplicationFactory {
authority,
appOwner,
templateHash,
dataAvailability,
salt
);
}
Expand All @@ -71,6 +73,7 @@ contract SelfHostedApplicationFactory is ISelfHostedApplicationFactory {
uint256 epochLength,
address appOwner,
bytes32 templateHash,
bytes calldata dataAvailability,
bytes32 salt
) external view returns (address application, address authority) {
authority = _authorityFactory.calculateAuthorityAddress(
Expand All @@ -83,6 +86,7 @@ contract SelfHostedApplicationFactory is ISelfHostedApplicationFactory {
IConsensus(authority),
appOwner,
templateHash,
dataAvailability,
salt
);
}
Expand Down
3 changes: 3 additions & 0 deletions contracts/inputs/IInputBox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,7 @@ interface IInputBox {
address appContract,
uint256 index
) external view returns (bytes32);

/// @notice Get number of block in which contract was deployed
function getDeploymentBlockNumber() external view returns (uint256);
}
17 changes: 17 additions & 0 deletions contracts/inputs/InputBox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@ import {CanonicalMachine} from "../common/CanonicalMachine.sol";
import {Inputs} from "../common/Inputs.sol";

contract InputBox is IInputBox {
/// @notice Deployment block number
uint256 immutable _deploymentBlockNumber;

/// @notice Mapping of application contract addresses to arrays of input hashes.
mapping(address => bytes32[]) private _inputBoxes;

constructor() {
_deploymentBlockNumber = block.number;
}

/// @inheritdoc IInputBox
function addInput(
address appContract,
Expand Down Expand Up @@ -65,4 +72,14 @@ contract InputBox is IInputBox {
) external view override returns (bytes32) {
return _inputBoxes[appContract][index];
}

/// @inheritdoc IInputBox
function getDeploymentBlockNumber()
external
view
override
returns (uint256)
{
return _deploymentBlockNumber;
}
}
26 changes: 22 additions & 4 deletions test/dapp/Application.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import {Outputs} from "contracts/common/Outputs.sol";
import {SafeERC20Transfer} from "contracts/delegatecall/SafeERC20Transfer.sol";
import {IOwnable} from "contracts/access/IOwnable.sol";
import {LibAddress} from "contracts/library/LibAddress.sol";
import {InputBox} from "contracts/inputs/InputBox.sol";
import {IInputBox} from "contracts/inputs/IInputBox.sol";
import {DataAvailability} from "contracts/common/DataAvailability.sol";

import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
Expand Down Expand Up @@ -45,12 +48,14 @@ contract ApplicationTest is TestBase, OwnableTest {
IERC1155 _erc1155SingleToken;
IERC1155 _erc1155BatchToken;
SafeERC20Transfer _safeERC20Transfer;
IInputBox _inputBox;

LibEmulator.State _emulator;
address _appOwner;
address _authorityOwner;
address _recipient;
address _tokenOwner;
bytes _dataAvailability;
string[] _outputNames;
bytes4[] _interfaceIds;
uint256[] _tokenIds;
Expand Down Expand Up @@ -90,13 +95,14 @@ contract ApplicationTest is TestBase, OwnableTest {
address(0)
)
);
new Application(_consensus, address(0), _templateHash);
new Application(_consensus, address(0), _templateHash, new bytes(0));
}

function testConstructor(
IConsensus consensus,
address owner,
bytes32 templateHash
bytes32 templateHash,
bytes calldata dataAvailability
) external {
vm.assume(owner != address(0));

Expand All @@ -106,12 +112,14 @@ contract ApplicationTest is TestBase, OwnableTest {
IApplication appContract = new Application(
consensus,
owner,
templateHash
templateHash,
dataAvailability
);

assertEq(address(appContract.getConsensus()), address(consensus));
assertEq(appContract.owner(), owner);
assertEq(appContract.getTemplateHash(), templateHash);
assertEq(appContract.getDataAvailability(), dataAvailability);
}

// -------------------
Expand Down Expand Up @@ -338,8 +346,18 @@ contract ApplicationTest is TestBase, OwnableTest {
_tokenIds,
_initialSupplies
);
_inputBox = new InputBox();
_consensus = new Authority(_authorityOwner, _epochLength);
_appContract = new Application(_consensus, _appOwner, _templateHash);
_dataAvailability = abi.encodeCall(
DataAvailability.InputBox,
(_inputBox)
);
_appContract = new Application(
_consensus,
_appOwner,
_templateHash,
_dataAvailability
);
_safeERC20Transfer = new SafeERC20Transfer();
}

Expand Down
Loading
Loading