-
Notifications
You must be signed in to change notification settings - Fork 38
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
17 changed files
with
2,633 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
node_modules | ||
.env | ||
coverage | ||
coverage.json | ||
typechain | ||
typechain-types | ||
|
||
# Hardhat files | ||
cache | ||
artifacts | ||
deployments | ||
|
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,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2023 Nick Johnson | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
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,88 @@ | ||
# @ensdomains/scroll-verifier | ||
|
||
A complete Solidity library that facilitates sending CCIP-Read requests for Scroll state, and verifying the responses. | ||
|
||
For a detailed readme and usage instructions, see the [monorepo readme](https://github.com/ensdomains/evmgateway/tree/main). | ||
|
||
## Testing | ||
|
||
Start up a devnet by following Scrolls's instructions [here](https://docs.scroll.io/node-running/how-tos/local-dev-node). | ||
|
||
The test requires you to use the rollup address of your node, which may not always be the same. This address is printed in the logs after you've initially set up the node. Copy that value and replace it accordingly. Unfortunately, there is no endpoint to retrieve the rollup address dynamically. | ||
|
||
Copy the rollup address from the Node's Logs. Add it to the following files | ||
|
||
``` | ||
scroll-verifier/test/testScrollVerifier.ts | ||
``` | ||
|
||
``` | ||
scroll-verifier/deploy_l1/00_scroll_verifier.ts | ||
``` | ||
|
||
Build the project | ||
|
||
``` | ||
bun run build | ||
``` | ||
|
||
Open another terminal window and start the Gateway | ||
|
||
``` | ||
cd ./scroll-gateway && bun run start -u http://127.0.0.1:8545/ -v http://127.0.0.1:8547/ -o $ROLLUP_ADDRESS -p 8089 | ||
``` | ||
|
||
Go back to the first Termina window and deploy the contracts to the test node | ||
|
||
``` | ||
npx hardhat --network scrollDevnetL1 deploy && npx hardhat --network scrollDevnetL2 deploy | ||
``` | ||
|
||
Finally, run the tests: | ||
|
||
``` | ||
bun run test | ||
``` | ||
|
||
## Deployments | ||
|
||
### Goerli | ||
|
||
#### L2 | ||
|
||
- TestL2.sol = [0xAdef74372444e716C0473dEe1F9Cb3108EFa3818](https://goerli.scrollscan.dev/address/0xAdef74372444e716C0473dEe1F9Cb3108EFa3818#code) | ||
|
||
#### L1 | ||
|
||
- ScrollVerifier = [0x9E46DeE08Ad370bEFa7858c0E9a6c87f2D7E57A1](https://goerli.etherscan.io/address/0x9E46DeE08Ad370bEFa7858c0E9a6c87f2D7E57A1#code) | ||
|
||
- TestL1.sol = [0x0d6c6B70cd561EB59e6818D832197fFad60840AB](https://goerli.etherscan.io/address/0x0d6c6B70cd561EB59e6818D832197fFad60840AB#code) | ||
|
||
#### Gateway server | ||
|
||
- https://scroll-gateway-worker.ens-cf.workers.dev | ||
|
||
|
||
### Sepolia | ||
|
||
#### L2 | ||
|
||
- TestL2.sol = [0x162A433068F51e18b7d13932F27e66a3f99E6890](https://api-sepolia.scrollscan.dev/address/0x162A433068F51e18b7d13932F27e66a3f99E6890#code) | ||
|
||
#### L1 | ||
|
||
- ScrollVerifier = [0x6820E47CED34D6F275c6d26C3876D48B2c1fdf27](https://sepolia.etherscan.io/address/0x6820E47CED34D6F275c6d26C3876D48B2c1fdf27#code) | ||
- TestL1.sol = [0x50200c7Ccb1abD927184396547ea8dD1A18CAA3A](https://sepolia.etherscan.io/address/0x50200c7Ccb1abD927184396547ea8dD1A18CAA3A#code) | ||
|
||
deploying "ScrollVerifier" (tx: 0x61ae88749f911f1e09d7c073f34a13bb843c71fafaf93a1266423798bd3aadc6)...: deployed at 0x6820E47CED34D6F275c6d26C3876D48B2c1fdf27 with 3872186 gas | ||
deploying "TestL1" (tx: 0x0a7b6b74357d20f33cb89df12da3db34b5cd3c764403888420108ca13f0126fa)...: deployed at 0x50200c7Ccb1abD927184396547ea8dD1A18CAA3A with 2411152 gas | ||
|
||
#### Gateway url | ||
|
||
- https://scroll-sepolia-gateway-worker.ens-cf.workers.dev | ||
|
||
## Testing gateway | ||
|
||
``` | ||
TARGET_ADDRESS=$TEST_L1_ADDRESS PROVIDER_URL=$L1_PROVIDER_URL npx hardhat run ../l1-verifier/scripts/remote.ts --network sepolia | ||
``` |
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,6 @@ | ||
module.exports = [ | ||
[ | ||
'https://scroll-sepolia-gateway-worker.ens-cf.workers.dev/{sender}/{data}.json' | ||
], | ||
'0xd80810638dbDF9081b72C1B33c65375e807281C8' | ||
]; |
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,89 @@ | ||
//SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.17; | ||
import {StateProof, EVMProofHelper} from '@ensdomains/evm-verifier/contracts/EVMProofHelper.sol'; | ||
import {IEVMVerifier} from '@ensdomains/evm-verifier/contracts/IEVMVerifier.sol'; | ||
import {Node, IRollupCore} from '@scroll/nitro-contracts/src/rollup/IRollupCore.sol'; | ||
import {RLPReader} from '@eth-optimism/contracts-bedrock/src/libraries/rlp/RLPReader.sol'; | ||
|
||
struct ScrollWitnessData { | ||
bytes32 version; | ||
bytes32 sendRoot; | ||
uint64 nodeIndex; | ||
bytes rlpEncodedBlock; | ||
} | ||
|
||
contract ScrollVerifier is IEVMVerifier { | ||
IRollupCore public immutable rollup; | ||
string[] _gatewayURLs; | ||
|
||
constructor(string[] memory _urls, IRollupCore _rollupAddress) { | ||
rollup = _rollupAddress; | ||
_gatewayURLs = _urls; | ||
} | ||
|
||
/* | ||
* Retrieves an array of gateway URLs used by the contract. | ||
* @returns {string[]} An array containing the gateway URLs. | ||
* */ | ||
function gatewayURLs() external view returns (string[] memory) { | ||
return _gatewayURLs; | ||
} | ||
|
||
/* | ||
* Retrieves storage values from the specified target address | ||
* | ||
* @param {address} target - The target address from which storage values are to be retrieved. | ||
* @param {bytes32[]} commands - An array of storage keys (commands) to query. | ||
* @param {bytes[]} constants - An array of constant values corresponding to the storage keys. | ||
* @param {bytes} proof - The proof data containing Scroll witness data and state proof. | ||
*/ | ||
function getStorageValues( | ||
address target, | ||
bytes32[] memory commands, | ||
bytes[] memory constants, | ||
bytes memory proof | ||
) external view returns (bytes[] memory values) { | ||
(ScrollWitnessData memory scrollData, StateProof memory stateProof) = abi | ||
.decode(proof, (ScrollWitnessData, StateProof)); | ||
|
||
//Get the node from the rollup contract | ||
Node memory rblock = rollup.getNode(scrollData.nodeIndex); | ||
|
||
//The confirm data is the keccak256 hash of the block hash and the send root. It is used to verify that the rblock is a subject of the layer 2 block that is being proven. | ||
bytes32 confirmData = keccak256( | ||
abi.encodePacked( | ||
keccak256(scrollData.rlpEncodedBlock), | ||
scrollData.sendRoot | ||
) | ||
); | ||
|
||
//Verify that the block hash is correct | ||
require(rblock.confirmData == confirmData, 'confirmData mismatch'); | ||
//Verifiy that the block that is being proven is the same as the block that was passed in | ||
|
||
//Now that we know that the block is valid, we can get the state root from the block. | ||
bytes32 stateRoot = getStateRootFromBlock(scrollData.rlpEncodedBlock); | ||
|
||
values = EVMProofHelper.getStorageValues( | ||
target, | ||
commands, | ||
constants, | ||
stateRoot, | ||
stateProof | ||
); | ||
} | ||
|
||
/* | ||
* Decodes a block by extracting and converting the bytes32 value from the RLP-encoded block to get the stateRoot. | ||
* | ||
* @param {bytes} rlpEncodedBlock - The RLP-encoded block information. | ||
* @returns {bytes32} The stateRoot extracted from the RLP-encoded block information. | ||
*/ | ||
function getStateRootFromBlock( | ||
bytes memory rlpEncodedBlock | ||
) internal pure returns (bytes32) { | ||
RLPReader.RLPItem[] memory i = RLPReader.readList(rlpEncodedBlock); | ||
//StateRoot is located at idx 3 | ||
return bytes32(RLPReader.readBytes(i[3])); | ||
} | ||
} |
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,106 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.17; | ||
|
||
import { EVMFetcher } from '@ensdomains/evm-verifier/contracts/EVMFetcher.sol'; | ||
import { EVMFetchTarget } from '@ensdomains/evm-verifier/contracts/EVMFetchTarget.sol'; | ||
import { IEVMVerifier } from '@ensdomains/evm-verifier/contracts/IEVMVerifier.sol'; | ||
|
||
contract TestL1 is EVMFetchTarget { | ||
using EVMFetcher for EVMFetcher.EVMFetchRequest; | ||
|
||
IEVMVerifier verifier; // Slot 0 | ||
address target; | ||
|
||
constructor(IEVMVerifier _verifier, address _target) { | ||
verifier = _verifier; | ||
target = _target; | ||
} | ||
|
||
function getLatest() public view returns(uint256) { | ||
EVMFetcher.newFetchRequest(verifier, target) | ||
.getStatic(0) | ||
.fetch(this.getLatestCallback.selector, ""); | ||
} | ||
|
||
function getLatestCallback(bytes[] memory values, bytes memory) public pure returns(uint256) { | ||
return abi.decode(values[0], (uint256)); | ||
} | ||
|
||
function getName() public view returns(string memory) { | ||
EVMFetcher.newFetchRequest(verifier, target) | ||
.getDynamic(1) | ||
.fetch(this.getNameCallback.selector, ""); | ||
} | ||
|
||
function getNameCallback(bytes[] memory values, bytes memory) public pure returns(string memory) { | ||
return string(values[0]); | ||
} | ||
|
||
function getHighscorer(uint256 idx) public view returns(string memory) { | ||
EVMFetcher.newFetchRequest(verifier, target) | ||
.getDynamic(3) | ||
.element(idx) | ||
.fetch(this.getHighscorerCallback.selector, ""); | ||
} | ||
|
||
function getHighscorerCallback(bytes[] memory values, bytes memory) public pure returns(string memory) { | ||
return string(values[0]); | ||
} | ||
|
||
function getLatestHighscore() public view returns(uint256) { | ||
EVMFetcher.newFetchRequest(verifier, target) | ||
.getStatic(0) | ||
.getStatic(2) | ||
.ref(0) | ||
.fetch(this.getLatestHighscoreCallback.selector, ""); | ||
} | ||
|
||
function getLatestHighscoreCallback(bytes[] memory values, bytes memory) public pure returns(uint256) { | ||
return abi.decode(values[1], (uint256)); | ||
} | ||
|
||
function getLatestHighscorer() public view returns(string memory) { | ||
EVMFetcher.newFetchRequest(verifier, target) | ||
.getStatic(0) | ||
.getDynamic(3) | ||
.ref(0) | ||
.fetch(this.getLatestHighscorerCallback.selector, ""); | ||
} | ||
|
||
function getLatestHighscorerCallback(bytes[] memory values, bytes memory) public pure returns(string memory) { | ||
return string(values[1]); | ||
} | ||
|
||
function getNickname(string memory _name) public view returns(string memory) { | ||
EVMFetcher.newFetchRequest(verifier, target) | ||
.getDynamic(4) | ||
.element(_name) | ||
.fetch(this.getNicknameCallback.selector, ""); | ||
} | ||
|
||
function getNicknameCallback(bytes[] memory values, bytes memory) public pure returns (string memory) { | ||
return string(values[0]); | ||
} | ||
|
||
function getPrimaryNickname() public view returns(string memory) { | ||
EVMFetcher.newFetchRequest(verifier, target) | ||
.getDynamic(1) | ||
.getDynamic(4) | ||
.ref(0) | ||
.fetch(this.getPrimaryNicknameCallback.selector, ""); | ||
} | ||
|
||
function getPrimaryNicknameCallback(bytes[] memory values, bytes memory) public pure returns (string memory) { | ||
return string(values[1]); | ||
} | ||
|
||
function getZero() public view returns(uint256) { | ||
EVMFetcher.newFetchRequest(verifier, target) | ||
.getStatic(5) | ||
.fetch(this.getZeroCallback.selector, ""); | ||
} | ||
|
||
function getZeroCallback(bytes[] memory values, bytes memory) public pure returns (uint256) { | ||
return abi.decode(values[0], (uint256)); | ||
} | ||
} |
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,21 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.17; | ||
|
||
contract TestL2 { | ||
uint256 latest; // Slot 0 | ||
string name; // Slot 1 | ||
mapping(uint256=>uint256) highscores; // Slot 2 | ||
mapping(uint256=>string) highscorers; // Slot 3 | ||
mapping(string=>string) realnames; // Slot 4 | ||
uint256 zero; // Slot 5 | ||
|
||
constructor() { | ||
latest = 42; | ||
name = "Satoshi"; | ||
highscores[latest] = 12345; | ||
highscorers[latest] = "Hal Finney"; | ||
highscorers[1] = "Hubert Blaine Wolfeschlegelsteinhausenbergerdorff Sr."; | ||
realnames["Money Skeleton"] = "Vitalik Buterin"; | ||
realnames["Satoshi"] = "Hal Finney"; | ||
} | ||
} |
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,37 @@ | ||
import { DeployFunction } from 'hardhat-deploy/types'; | ||
import { HardhatRuntimeEnvironment } from 'hardhat/types'; | ||
import 'dotenv/config'; | ||
|
||
const GATEWAY_URLS = { | ||
scrollDevnetL1: 'http://localhost:8089/{sender}/{data}.json', | ||
goerli: 'https://scroll-gateway-worker.ens-cf.workers.dev/{sender}/{data}.json', | ||
sepolia: 'https://scroll-sepolia-gateway-worker.ens-cf.workers.dev/{sender}/{data}.json', | ||
}; | ||
|
||
const ROLLUP_ADDRESSES = { | ||
goerli: '0x45e5cAea8768F42B385A366D3551Ad1e0cbFAb17', | ||
sepolia: '0xd80810638dbDF9081b72C1B33c65375e807281C8', | ||
}; | ||
|
||
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { | ||
const { deployments, getNamedAccounts, network } = hre; | ||
const { deploy } = deployments; | ||
const { deployer } = await getNamedAccounts(); | ||
let ROLLUP_ADDRESS; | ||
if (network.name === 'scrollDevnetL1') { | ||
//Rollup address according to sequencer config. Unfortunately, there is no endpoint to fetch it at runtime from the rollup. | ||
//The address can be found at nitro-testnode-sequencer-1/config/deployment.json | ||
ROLLUP_ADDRESS = process.env.ROLLUP_ADDRESS; | ||
} else { | ||
ROLLUP_ADDRESS = ROLLUP_ADDRESSES[network.name]; | ||
} | ||
const GATEWAY_URL = GATEWAY_URLS[network.name]; | ||
console.log('ScrollVerifier', [[GATEWAY_URL], ROLLUP_ADDRESS]); | ||
await deploy('ScrollVerifier', { | ||
from: deployer, | ||
args: [[GATEWAY_URL], ROLLUP_ADDRESS], | ||
log: true, | ||
}); | ||
}; | ||
export default func; | ||
func.tags = ['ScrollVerifier']; |
Oops, something went wrong.