Skip to content
Merged
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
3 changes: 2 additions & 1 deletion scripts/config.example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {

type PartialRollupParams = Pick<
RollupCreator.RollupDeploymentParamsStruct,
'config' | 'validators' | 'batchPosterManager' | 'batchPosters'
'config' | 'validators' | 'batchPosterManager' | 'batchPosters' | 'customOsp'
>

// 90% of Geth's 128KB tx size limit, leaving ~13KB for proving
Expand Down Expand Up @@ -68,6 +68,7 @@ export const config: PartialRollupParams = {
],
batchPosterManager: '0x1234123412341234123412341234123412341234',
batchPosters: ['0x1234123412341234123412341234123412341234'],
customOsp: ethers.constants.AddressZero,
}

// These value should be defined in the env
Expand Down
8 changes: 7 additions & 1 deletion scripts/createERC20Rollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ async function main() {
feeTokenPricer = ethers.constants.AddressZero
}

let customOsp = process.env.CUSTOM_OSP_ADDRESS as string
if (!customOsp) {
customOsp = ethers.constants.AddressZero
}

console.log(
'Creating new rollup with',
customFeeTokenAddress,
Expand All @@ -56,7 +61,8 @@ async function main() {
rollupCreatorAddress,
customFeeTokenAddress,
feeTokenPricer,
stakeTokenAddress
stakeTokenAddress,
customOsp
)
}

Expand Down
8 changes: 7 additions & 1 deletion scripts/createEthRollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ async function main() {
throw new Error('STAKE_TOKEN_ADDRESS not set')
}

let customOsp = process.env.CUSTOM_OSP_ADDRESS as string
if (!customOsp) {
customOsp = ethers.constants.AddressZero
}

const [signer] = await ethers.getSigners()

await createRollup(
Expand All @@ -23,7 +28,8 @@ async function main() {
rollupCreatorAddress,
feeToken,
feeTokenPricer,
stakeTokenAddress
stakeTokenAddress,
customOsp
)
}

Expand Down
8 changes: 7 additions & 1 deletion scripts/local-deployment/deployCreatorAndCreateRollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ async function main() {
console.log('WETH deployed at', stakeToken)
}

let customOsp = process.env.CUSTOM_OSP_ADDRESS as string
if (!customOsp) {
customOsp = ethers.constants.AddressZero
}

const factoryCode = await deployerWallet.provider.getCode(
'0x4e59b44847b379578588920ca78fbf26c0b4956c'
)
Expand Down Expand Up @@ -116,7 +121,8 @@ async function main() {
contracts.rollupCreator.address,
feeToken,
feeTokenPricer,
stakeToken
stakeToken,
customOsp
)

if (!result) {
Expand Down
15 changes: 11 additions & 4 deletions scripts/rollupCreation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ export async function createRollup(
rollupCreatorAddress: string,
feeToken: string,
feeTokenPricer: string,
stakeToken: string
stakeToken: string,
customOsp: string
): Promise<{
rollupCreationResult: RollupCreationResult
chainInfo: ChainInfo
Expand Down Expand Up @@ -112,7 +113,8 @@ export async function createRollup(
feeToken,
feeTokenPricer,
validatorWalletCreator,
stakeToken
stakeToken,
customOsp
)
: {
config: config.config,
Expand All @@ -124,9 +126,12 @@ export async function createRollup(
batchPosters: config.batchPosters,
batchPosterManager: config.batchPosterManager,
feeTokenPricer: feeTokenPricer,
customOsp: config.customOsp,
}

const createRollupTx = await rollupCreator.createRollup(deployParams, {
const createRollupTx = await rollupCreator.functions[
'createRollup(((uint64,address,uint256,bytes32,address,address,uint256,string,uint256,uint64,uint256[],(uint256,uint256,uint256,uint256),uint256,uint256,uint256,((bytes32[2],uint64[2]),uint8,bytes32),uint256,address,uint8,uint64,(uint64,uint64,uint64)),address[],uint256,address,bool,uint256,address[],address,address,address))'
](deployParams, {
value: feeCost,
})
const createRollupReceipt = await createRollupTx.wait()
Expand Down Expand Up @@ -235,7 +240,8 @@ async function _getDevRollupConfig(
feeToken: string,
feeTokenPricer: string,
validatorWalletCreator: string,
stakeToken: string
stakeToken: string,
customOsp: string
): Promise<RollupCreator.RollupDeploymentParamsStruct> {
// set up owner address
const ownerAddress =
Expand Down Expand Up @@ -357,6 +363,7 @@ async function _getDevRollupConfig(
batchPosters: batchPosters,
batchPosterManager: batchPosterManager,
feeTokenPricer: feeTokenPricer,
customOsp: customOsp,
}

function _createValidatorAddress(
Expand Down
2 changes: 1 addition & 1 deletion slither.db.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/bridge/ISequencerInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ interface ISequencerInbox is IDelayedMessageProvider {
// solhint-disable-next-line func-name-mixedcase
function ZERO_HEAVY_MESSAGE_HEADER_FLAG() external view returns (bytes1);

/// @dev If the first data byte after the header has this set,
/// then the batch data comes from a custom data availability provider
// solhint-disable-next-line func-name-mixedcase
function CUSTOM_DA_MESSAGE_HEADER_FLAG() external view returns (bytes1);

function rollup() external view returns (IOwnable);

function isBatchPoster(
Expand Down
6 changes: 5 additions & 1 deletion src/bridge/SequencerInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
/// @inheritdoc ISequencerInbox
bytes1 public constant ZERO_HEAVY_MESSAGE_HEADER_FLAG = 0x20;

/// @inheritdoc ISequencerInbox
bytes1 public constant CUSTOM_DA_MESSAGE_HEADER_FLAG = 0x01;

// GAS_PER_BLOB from EIP-4844
uint256 internal constant GAS_PER_BLOB = 1 << 17;

Expand Down Expand Up @@ -599,7 +602,8 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
) internal pure returns (bool) {
return headerByte == BROTLI_MESSAGE_HEADER_FLAG || headerByte == DAS_MESSAGE_HEADER_FLAG
|| (headerByte == (DAS_MESSAGE_HEADER_FLAG | TREE_DAS_MESSAGE_HEADER_FLAG))
|| headerByte == ZERO_HEAVY_MESSAGE_HEADER_FLAG;
|| headerByte == ZERO_HEAVY_MESSAGE_HEADER_FLAG
|| headerByte == CUSTOM_DA_MESSAGE_HEADER_FLAG;
}

/// @dev Form a hash of the data taken from the calldata
Expand Down
51 changes: 38 additions & 13 deletions src/osp/ReferenceDAProofValidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,62 @@ import "./ICustomDAProofValidator.sol";
contract ReferenceDAProofValidator is ICustomDAProofValidator {
/**
* @notice Validates a ReferenceDA proof and returns the preimage chunk
* @param proof ReferenceDA proof format: [hash(32), offset(8), Version(1), PreimageSize(8), PreimageData]
* @param proof ReferenceDA proof format: [certKeccak256(32), offset(8), Version(1), CertificateSize(8), Certificate, PreimageSize(8), PreimageData]
* @return preimageChunk The 32-byte chunk at the specified offset
*/
function validateReadPreimage(
bytes calldata proof
) external pure override returns (bytes memory preimageChunk) {
// ReferenceDA proof format: [hash(32), offset(8), Version(1), PreimageSize(8), PreimageData]
require(proof.length >= 49, "Proof too short"); // 32 + 8 + 1 + 8
// Proof format: [certKeccak256(32), offset(8), Version(1), CertificateSize(8), Certificate, PreimageSize(8), PreimageData]
require(proof.length >= 58, "Proof too short"); // 32 + 8 + 1 + 8 + 8 + at least 1 byte

// Extract hash and offset that were included by the off-chain enhancer
bytes32 hash;
// Extract certKeccak256 and offset from enhanced proof wrapper
bytes32 certKeccak256;
uint256 offset;
assembly {
hash := calldataload(add(proof.offset, 0))
certKeccak256 := calldataload(add(proof.offset, 0))
offset := shr(192, calldataload(add(proof.offset, 32))) // Read 8 bytes as uint256
}

// Decode the actual proof data
require(proof[40] == 0x01, "Unsupported proof version");
// The actual custom proof starts at offset 40
uint256 customProofStart = 40;

// Verify version
require(proof[customProofStart] == 0x01, "Unsupported proof version");

// Extract certificate size
uint256 certSize;
assembly {
certSize := shr(192, calldataload(add(proof.offset, add(customProofStart, 1)))) // Read 8 bytes as uint256
}
require(certSize == 33, "Certificate must be 33 bytes");

// Extract and verify certificate
uint256 certStart = customProofStart + 9; // Skip version(1) + certSize(8)
bytes memory certificate = proof[certStart:certStart + certSize];
require(certificate[0] == 0x01, "Invalid certificate header");
require(keccak256(certificate) == certKeccak256, "Invalid certificate hash");

// Extract SHA256 from certificate
bytes32 sha256Hash;
assembly {
sha256Hash := mload(add(certificate, 33)) // Skip length prefix and header byte
}

// Extract preimage size
uint256 preimageOffset = certStart + certSize;
uint256 preimageSize;
assembly {
preimageSize := shr(192, calldataload(add(proof.offset, 41))) // Read 8 bytes as uint256
preimageSize := shr(192, calldataload(add(proof.offset, preimageOffset))) // Read 8 bytes as uint256
}
require(proof.length == 49 + preimageSize, "Invalid proof length");

require(proof.length >= preimageOffset + 8 + preimageSize, "Invalid proof length");

// Extract preimage data
bytes memory preimage = proof[49:];
bytes memory preimage = proof[preimageOffset + 8:preimageOffset + 8 + preimageSize];

// Verify hash
require(keccak256(preimage) == hash, "Invalid preimage");
// Verify SHA256 hash matches
require(sha256(abi.encodePacked(preimage)) == sha256Hash, "Invalid preimage hash");

// Extract chunk at offset
uint256 chunkStart = offset;
Expand Down
50 changes: 46 additions & 4 deletions src/rollup/RollupCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.so
import "@openzeppelin/contracts/access/Ownable.sol";
import {DeployHelper} from "./DeployHelper.sol";
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {OneStepProofEntry, IOneStepProofEntry, IOneStepProver} from "../osp/OneStepProofEntry.sol";
import {OneStepProverHostIo} from "../osp/OneStepProverHostIo.sol";

contract RollupCreator is Ownable {
using SafeERC20 for IERC20;
Expand All @@ -32,6 +34,18 @@ contract RollupCreator is Ownable {
);
event TemplatesUpdated();

struct RollupDeploymentParamsLegacy {
Config config;
address[] validators;
uint256 maxDataSize;
address nativeToken;
bool deployFactoriesToL2;
uint256 maxFeePerGasForRetryables;
address[] batchPosters;
address batchPosterManager;
IFeeTokenPricer feeTokenPricer;
}

struct RollupDeploymentParams {
Config config;
address[] validators;
Expand All @@ -42,6 +56,7 @@ contract RollupCreator is Ownable {
address[] batchPosters;
address batchPosterManager;
IFeeTokenPricer feeTokenPricer;
address customOsp;
}

BridgeCreator public bridgeCreator;
Expand Down Expand Up @@ -107,7 +122,8 @@ contract RollupCreator is Ownable {
function createChallengeManager(
address rollupAddr,
address proxyAdminAddr,
Config memory config
Config memory config,
address customOsp
) internal returns (IEdgeChallengeManager) {
IEdgeChallengeManager challengeManager = IEdgeChallengeManager(
address(
Expand All @@ -120,7 +136,7 @@ contract RollupCreator is Ownable {
challengeManager.initialize({
_assertionChain: IAssertionChain(rollupAddr),
_challengePeriodBlocks: config.confirmPeriodBlocks,
_oneStepProofEntry: osp,
_oneStepProofEntry: customOsp == address(0) ? osp : IOneStepProofEntry(customOsp),
layerZeroBlockEdgeHeight: config.layerZeroBlockEdgeHeight,
layerZeroBigStepEdgeHeight: config.layerZeroBigStepEdgeHeight,
layerZeroSmallStepEdgeHeight: config.layerZeroSmallStepEdgeHeight,
Expand All @@ -133,6 +149,29 @@ contract RollupCreator is Ownable {
return challengeManager;
}

/**
* @notice Create a new rollup
* DEPRECATED, use the createRollup(RollupDeploymentParams) method instead
*/
function createRollup(
RollupDeploymentParamsLegacy memory deployParams
) public payable returns (address) {
return createRollup(
RollupDeploymentParams({
config: deployParams.config,
validators: deployParams.validators,
maxDataSize: deployParams.maxDataSize,
nativeToken: deployParams.nativeToken,
deployFactoriesToL2: deployParams.deployFactoriesToL2,
maxFeePerGasForRetryables: deployParams.maxFeePerGasForRetryables,
batchPosters: deployParams.batchPosters,
batchPosterManager: deployParams.batchPosterManager,
feeTokenPricer: deployParams.feeTokenPricer,
customOsp: address(0)
})
);
}

/**
* @notice Create a new rollup
* @dev After this setup:
Expand All @@ -154,6 +193,8 @@ contract RollupCreator is Ownable {
* - maxFeePerGasForRetryables price bid for L2 execution.
* - batchPosters The list of batch poster addresses, not used when set to empty list
* - batchPosterManager The address which has the ability to rotate batch poster keys
* - feeTokenPricer Fee token pricer contract for converting custom fee token to ETH
* - customOsp Custom OSP address, if set to address(0) the default OSP will be used
* @return The address of the newly created rollup
*/
function createRollup(
Expand Down Expand Up @@ -213,8 +254,9 @@ contract RollupCreator is Ownable {
deployParams.feeTokenPricer
);

IEdgeChallengeManager challengeManager =
createChallengeManager(address(rollup), address(proxyAdmin), deployParams.config);
IEdgeChallengeManager challengeManager = createChallengeManager(
address(rollup), address(proxyAdmin), deployParams.config, deployParams.customOsp
);

// deploy and init upgrade executor
address upgradeExecutor = _deployUpgradeExecutor(deployParams.config.owner, proxyAdmin);
Expand Down
11 changes: 8 additions & 3 deletions test/e2e/orbitChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -880,13 +880,18 @@ describe('Orbit Chain', () => {
deployFactoriesToL2,
maxFeePerGasForRetryables,
feeTokenPricer: ethers.constants.AddressZero,
customOsp: ethers.constants.AddressZero,
}

/// deploy it
const receipt = await (
await rollupCreator.connect(userL1Wallet).createRollup(deployParams, {
value: nativeToken ? BigNumber.from(0) : fee,
})
await rollupCreator
.connect(userL1Wallet)
[
'createRollup(((uint64,address,uint256,bytes32,address,address,uint256,string,uint256,uint64,uint256[],(uint256,uint256,uint256,uint256),uint256,uint256,uint256,((bytes32[2],uint64[2]),uint8,bytes32),uint256,address,uint8,uint64,(uint64,uint64,uint64)),address[],uint256,address,bool,uint256,address[],address,address,address))'
](deployParams, {
value: nativeToken ? BigNumber.from(0) : fee,
})
).wait()

const l1TxReceipt = new L1TransactionReceipt(receipt)
Expand Down
Loading