-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
985 additions
and
530 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,28 @@ | ||
import { defaultWagmiConfig } from "@web3modal/wagmi/react/config"; | ||
import { http, injected } from "@wagmi/core"; | ||
import { http } from "@wagmi/core"; | ||
import { mainnet, sepolia, linea, lineaSepolia } from "@wagmi/core/chains"; | ||
import { walletConnect, coinbaseWallet } from "@wagmi/connectors"; | ||
import { config } from "./config"; | ||
import { WagmiAdapter } from "@reown/appkit-adapter-wagmi"; | ||
import { AppKitNetwork } from "@reown/appkit/networks"; | ||
|
||
if (!config.walletConnectId) throw new Error("Project ID is not defined"); | ||
|
||
const metadata = { | ||
name: "Linea Bridge", | ||
description: `Linea Bridge is a bridge solution, providing secure and efficient cross-chain transactions between Layer 1 and Linea networks. | ||
Discover the future of blockchain interaction with Linea Bridge.`, | ||
url: "https://bridge.linea.build", | ||
icons: [], | ||
}; | ||
export const chains: [AppKitNetwork, ...AppKitNetwork[]] = [mainnet, sepolia, linea, lineaSepolia]; | ||
|
||
const chains = [mainnet, sepolia, linea, lineaSepolia] as const; | ||
|
||
export const wagmiConfig = defaultWagmiConfig({ | ||
chains, | ||
export const wagmiAdapter = new WagmiAdapter({ | ||
networks: chains, | ||
projectId: config.walletConnectId, | ||
metadata, | ||
multiInjectedProviderDiscovery: true, | ||
ssr: true, | ||
enableEIP6963: true, | ||
|
||
batch: { | ||
multicall: true, | ||
}, | ||
connectors: [ | ||
walletConnect({ | ||
projectId: config.walletConnectId, | ||
showQrModal: false, | ||
}), | ||
injected({ shimDisconnect: true }), | ||
coinbaseWallet({ | ||
appName: "Linea Bridge", | ||
}), | ||
], | ||
transports: { | ||
[mainnet.id]: http(`https://mainnet.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`, { batch: true }), | ||
[sepolia.id]: http(`https://sepolia.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`, { batch: true }), | ||
[linea.id]: http(`https://linea-mainnet.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`, { batch: true }), | ||
[lineaSepolia.id]: http(`https://linea-sepolia.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`, { batch: true }), | ||
}, | ||
}); | ||
|
||
export const wagmiConfig = wagmiAdapter.wagmiConfig; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ coverage.json | |
typechain | ||
typechain-types | ||
cache | ||
cache_forge | ||
build | ||
.openzeppelin | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -167,4 +167,4 @@ pnpm run test | |
pnpm run test:reportgas | ||
pnpm run coverage | ||
``` | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
// SPDX-License-Identifier: AGPL-3.0 | ||
pragma solidity 0.8.26; | ||
|
||
import "forge-std/Test.sol"; | ||
import "../../contracts/LineaRollup.sol"; | ||
import "../../contracts/interfaces/l1/ILineaRollup.sol"; | ||
import "../../contracts/lib/Utils.sol"; | ||
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; | ||
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; | ||
|
||
contract LineaRollupTestHelper is LineaRollup { | ||
function calculateY(bytes calldata data, bytes32 dataEvaluationPoint) external pure returns (bytes32) { | ||
return _calculateY(data, dataEvaluationPoint); | ||
} | ||
} | ||
|
||
contract LineaRollupTest is Test { | ||
LineaRollupTestHelper lineaRollup; | ||
LineaRollupTestHelper implementation; | ||
address operator; | ||
address defaultAdmin; | ||
address verifier; | ||
address nonAuthorizedAccount; | ||
address securityCouncil; | ||
address fallbackOperator; | ||
|
||
bytes32 VERIFIER_SETTER_ROLE; | ||
bytes32 VERIFIER_UNSETTER_ROLE; | ||
bytes32 OPERATOR_ROLE; | ||
bytes32 DEFAULT_ADMIN_ROLE; | ||
|
||
function setUp() public { | ||
operator = address(0x1); | ||
defaultAdmin = address(0x2); | ||
verifier = address(0x3); | ||
securityCouncil = defaultAdmin; | ||
fallbackOperator = address(0x4); | ||
nonAuthorizedAccount = address(0x5); | ||
|
||
implementation = new LineaRollupTestHelper(); | ||
|
||
ILineaRollup.InitializationData memory initData; | ||
initData.initialStateRootHash = bytes32(0x0); | ||
initData.initialL2BlockNumber = 0; | ||
initData.genesisTimestamp = block.timestamp; | ||
initData.defaultVerifier = verifier; | ||
initData.rateLimitPeriodInSeconds = 86400; // 1 day | ||
initData.rateLimitAmountInWei = 100 ether; | ||
|
||
initData.roleAddresses = new IPermissionsManager.RoleAddress[](1); | ||
initData.roleAddresses[0] = IPermissionsManager.RoleAddress({ | ||
addressWithRole: operator, | ||
role: implementation.OPERATOR_ROLE() | ||
}); | ||
|
||
initData.pauseTypeRoles = new IPauseManager.PauseTypeRole[](0); | ||
initData.unpauseTypeRoles = new IPauseManager.PauseTypeRole[](0); | ||
initData.fallbackOperator = fallbackOperator; | ||
initData.defaultAdmin = defaultAdmin; | ||
|
||
bytes memory initializer = abi.encodeWithSelector( | ||
LineaRollup.initialize.selector, | ||
initData | ||
); | ||
|
||
ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), initializer); | ||
|
||
lineaRollup = LineaRollupTestHelper(address(proxy)); | ||
|
||
VERIFIER_SETTER_ROLE = lineaRollup.VERIFIER_SETTER_ROLE(); | ||
VERIFIER_UNSETTER_ROLE = lineaRollup.VERIFIER_UNSETTER_ROLE(); | ||
OPERATOR_ROLE = lineaRollup.OPERATOR_ROLE(); | ||
DEFAULT_ADMIN_ROLE = lineaRollup.DEFAULT_ADMIN_ROLE(); | ||
|
||
assertEq(lineaRollup.hasRole(DEFAULT_ADMIN_ROLE, defaultAdmin), true, "Default admin not set"); | ||
assertEq(lineaRollup.hasRole(OPERATOR_ROLE, operator), true, "Operator not set"); | ||
} | ||
|
||
function testSubmitDataAsCalldata() public { | ||
ILineaRollup.CompressedCalldataSubmission memory submission; | ||
submission.finalStateRootHash = keccak256(abi.encodePacked("finalStateRootHash")); | ||
submission.snarkHash = keccak256(abi.encodePacked("snarkHash")); | ||
|
||
// Adjust compressedData to start with 0x00 | ||
submission.compressedData = hex"00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"; | ||
|
||
bytes32 dataEvaluationPoint = Utils._efficientKeccak( | ||
submission.snarkHash, | ||
keccak256(submission.compressedData) | ||
); | ||
|
||
bytes32 dataEvaluationClaim = lineaRollup.calculateY(submission.compressedData, dataEvaluationPoint); | ||
|
||
bytes32 parentShnarf = lineaRollup.GENESIS_SHNARF(); | ||
|
||
bytes32 expectedShnarf = keccak256( | ||
abi.encodePacked( | ||
parentShnarf, | ||
submission.snarkHash, | ||
submission.finalStateRootHash, | ||
dataEvaluationPoint, | ||
dataEvaluationClaim | ||
) | ||
); | ||
|
||
vm.prank(operator); | ||
lineaRollup.submitDataAsCalldata(submission, parentShnarf, expectedShnarf); | ||
|
||
uint256 exists = lineaRollup.blobShnarfExists(expectedShnarf); | ||
assertEq(exists, 1, "Blob shnarf should exist after submission"); | ||
} | ||
|
||
function testChangeVerifierNotAuthorized() public { | ||
address newVerifier = address(0x1234); | ||
|
||
vm.prank(nonAuthorizedAccount); | ||
vm.expectRevert( | ||
abi.encodePacked( | ||
"AccessControl: account ", | ||
_toAsciiString(nonAuthorizedAccount), | ||
" is missing role ", | ||
_toHexString(VERIFIER_SETTER_ROLE) | ||
) | ||
); | ||
lineaRollup.setVerifierAddress(newVerifier, 2); | ||
} | ||
|
||
function testSetVerifierAddressSuccess() public { | ||
vm.startPrank(defaultAdmin); | ||
lineaRollup.grantRole(VERIFIER_SETTER_ROLE, defaultAdmin); | ||
vm.stopPrank(); | ||
|
||
address newVerifier = address(0x1234); | ||
|
||
vm.prank(defaultAdmin); | ||
lineaRollup.setVerifierAddress(newVerifier, 2); | ||
|
||
assertEq(lineaRollup.verifiers(2), newVerifier, "Verifier address not updated"); | ||
} | ||
|
||
function testUnsetVerifierAddress() public { | ||
vm.startPrank(defaultAdmin); | ||
lineaRollup.grantRole(VERIFIER_UNSETTER_ROLE, defaultAdmin); | ||
|
||
lineaRollup.grantRole(VERIFIER_SETTER_ROLE, defaultAdmin); | ||
|
||
address newVerifier = address(0x1234); | ||
lineaRollup.setVerifierAddress(newVerifier, 0); | ||
vm.stopPrank(); | ||
|
||
vm.prank(defaultAdmin); | ||
lineaRollup.unsetVerifierAddress(0); | ||
|
||
assertEq(lineaRollup.verifiers(0), address(0), "Verifier address not unset"); | ||
} | ||
|
||
function testUnsetVerifierNotAuthorized() public { | ||
vm.prank(nonAuthorizedAccount); | ||
vm.expectRevert( | ||
abi.encodePacked( | ||
"AccessControl: account ", | ||
_toAsciiString(nonAuthorizedAccount), | ||
" is missing role ", | ||
_toHexString(VERIFIER_UNSETTER_ROLE) | ||
) | ||
); | ||
lineaRollup.unsetVerifierAddress(0); | ||
} | ||
|
||
// Helper function to convert address to ascii string | ||
function _toAsciiString(address x) internal pure returns (string memory) { | ||
bytes memory s = new bytes(42); | ||
s[0] = "0"; | ||
s[1] = "x"; | ||
for (uint256 i = 0; i < 20; i++) { | ||
uint8 b = uint8(uint256(uint160(x)) / (2**(8 * (19 - i)))); | ||
uint8 hi = b / 16; | ||
uint8 lo = b - 16 * hi; | ||
s[2 + 2 * i] = _char(hi); | ||
s[3 + 2 * i] = _char(lo); | ||
} | ||
return string(s); | ||
} | ||
|
||
// Helper function to convert byte to char | ||
function _char(uint8 b) internal pure returns (bytes1 c) { | ||
if (b < 10) { | ||
return bytes1(b + 0x30); | ||
} else { | ||
return bytes1(b + 0x57); | ||
} | ||
} | ||
|
||
// Helper function to convert bytes32 to hex string | ||
function _toHexString(bytes32 data) internal pure returns (string memory) { | ||
return _toHexString(abi.encodePacked(data)); | ||
} | ||
|
||
// Helper function to convert bytes to hex string | ||
function _toHexString(bytes memory data) internal pure returns (string memory) { | ||
bytes memory hexString = new bytes(data.length * 2 + 2); | ||
hexString[0] = "0"; | ||
hexString[1] = "x"; | ||
bytes memory hexChars = "0123456789abcdef"; | ||
for (uint256 i = 0; i < data.length; i++) { | ||
hexString[2 + i * 2] = hexChars[uint8(data[i] >> 4)]; | ||
hexString[3 + i * 2] = hexChars[uint8(data[i] & 0x0f)]; | ||
} | ||
return string(hexString); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.