-
Notifications
You must be signed in to change notification settings - Fork 39
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 config using ERC-165 #338
On-chain node config using ERC-165 #338
Conversation
Bonus: with this PR, we also save 13.3% gas when deploying |
Is there still any reason to exist a IDataAvailability interface? Maybe a method that every implementation must provide? |
In this case, we're using ERC-165 as the base interface. Besides |
Sorry, I really didn't understand how this would work for Espresso or any other source that requires additional parameters besides the DataAvailability contract address. For instance, where would one specify the namespace (or rollupId) and starting block? I thought the whole idea of the bytes signature was to avoid having every application deploy a new DataAvailability contract (although that's possible) |
I have the same question as @miltonjonat. If that is the case, that we would need each application to deploy its own espresso config smart contract just to signal this simple information, I am not sure the elegance of the solution is worth. I`m leaning towards having the information encoded in the application contract and dealing with the encoding/decoding in other layers, not on chain. |
This information would be accessible by calling functions from the DA contract. For example, an Espresso DA contract would have functions such as |
I did a little benchmark, and it seems that using ERC-165 is cheaper. First, I defined the Espresso DA interface: interface IEspressoDA {
function inputBox() external view returns (IInputBox);
function firstBlock() external view returns (uint256);
function namespace() external view returns (uint32);
} Then, I wrote a contract that implemented this interface and ERC-165. contract EspressoDA is IEspressoDA, ERC165 {
IInputBox immutable public inputBox;
uint256 immutable public firstBlock;
uint32 immutable public namespace;
constructor (IInputBox _inputBox, uint256 _firstBlock, uint32 _namespace) {
inputBox = _inputBox;
firstBlock = _firstBlock;
namespace = _namespace;
}
function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
return (interfaceId == type(IEspressoDA).interfaceId) || super.supportsInterface(interfaceId);
}
} Then I wrote a dummy app contract that stored an immutable address to the Espresso DA contract and another that stored a blob that encodes the same data, and benchmarked their deployments. contract AppWithAddress {
EspressoDA immutable public da;
constructor(EspressoDA _da) {
da = _da;
}
}
contract AppWithSig {
bytes public daSig;
constructor(bytes memory _daSig) {
daSig = _daSig;
}
}
contract FooTest is TestBase {
function testFoo(IInputBox inputBox, uint256 firstBlock, uint32 namespace) external {
EspressoDA da = new EspressoDA(inputBox, firstBlock, namespace);
da.inputBox();
da.firstBlock();
da.namespace();
AppWithAddress appWithAddress = new AppWithAddress(da);
appWithAddress.da();
AppWithSig appWithSig = new AppWithSig(abi.encodeWithSignature("Espresso(address,uint256,uint32)", inputBox, firstBlock, namespace));
appWithSig.daSig();
}
} The benchmark showed that it costed 145948 gas to deploy the DA contract, and 90024 gas to deploy the app contract with the DA contract address embedded in the bytecode. In total, 235972 gas. Meanwhile, deploying the app contract that stores the DA blob into storage costs 271163 gas, which is 15% more expensive. |
Regarding deployment front-end development and UX, we can streamline the process through factory contracts that deploy the DA contract + the application using the DA contract. |
In addition, to avoid the need of a contract for each application, you could also have a contract that supports multiple applications. |
Sorry, I am still confused haha. Why isn't this daSig immutable too, for a fair comparison? |
As of the latest release of Solidity, state variables of dynamic types (such as
Source: Solidity v0.8.28 Documentation |
It's important to note that, in order to support multiple applications, the contract, whose byte-code is made immutable upon construction, would need to store DA info in storage, which can be costly. |
Ok! So it works now because the DA use cases we currently have don't require any such dynamic type as a parameter. But I don't know if we'll ever have that, or if Solidity will support those anyway in the future. |
Yes, if one of the DA config parameters were of a dynamic type, such as |
Note that it's mainly a matter of gas costs. It would still work if one of the DA config parameters were of a dynamic type, but it would just be more expensive than embedding it into the contract bytecode as an immutable variable. |
Versus having to deploy a new instance for each dapp. |
I think I still haven't visualized how these factory contracts would work. I just want to make sure this doesn't complicate thinks for the CLI. |
@miltonjonat Here is a draft: interface IEspressoDA is IERC165 {
function inputBox() external view returns (IInputBox);
function firstBlock() external view returns (uint256);
function namespace() external view returns (uint32);
}
contract EspressoDA is IEspressoDA, ERC165 {
IInputBox immutable public inputBox;
uint256 immutable public firstBlock;
uint32 immutable public namespace;
constructor (IInputBox _inputBox, uint256 _firstBlock, uint32 _namespace) {
inputBox = _inputBox;
firstBlock = _firstBlock;
namespace = _namespace;
}
function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
return (interfaceId == type(IEspressoDA).interfaceId) ||
super.supportsInterface(interfaceId);
}
}
contract EspressoDAFactory {
function newContract(
IInputBox _inputBox,
uint256 _firstBlock,
uint32 _namespace,
bytes32 _salt
) external returns (IEspressoDA) {
return new EspressoDA{salt: _salt}(
_inputBox,
_firstBlock,
_namespace
);
}
} |
Ok @guidanoli , but I still don't understand how would the CLI (or other deployment tools) use this factory? Ideally we should minimize any logic that the CLI would need to add to handle Espresso (or any other service besides the standard ones). |
They would first deploy this DA contract, and then the application contract pointing to the DA contract. We could also create a factory that does both actions in the same transaction. |
This is just a draft PR on top of the main PR to show how the contracts could look like, following @tuler's suggestion of describing data availability solutions as ERC-165-compliant contracts.
This PR makes the
InputBox
contract itself implement the ERC-165 standard, and explicitly announce its support to theIInputBox
interface through this standard. Deployment front-ends would simply provide theInputBox
contract as the data availability contract of their application if they wish to use it as the only source of data availability.One can easily consult its interface ID by running the following commands on
chisel
, while on the repository root. Alternatively, one can also manually XOR all the function selectors defined in the interface.The output has type
bytes4
, so we can extract just the first 4 bytes, which gives us the interface ID0xa2f1daf5
.So, the node would call
supportsInterfaceId(0xa2f1daf5)
and check whether it returnstrue
. If so, then that means that the application uses this contract as its only source of data availability, and that this contract implements theIInputBox
interface.Something similar would have to be done for Espresso, but is beyond the scope of this repository.