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

Add arbi validator #19

Merged
merged 68 commits into from
Dec 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
09f558f
add playgorund for testing
AlexNi245 Nov 17, 2023
2ed2c13
mvp arbi proof service
AlexNi245 Nov 17, 2023
3481092
add arb-verifier package
AlexNi245 Nov 17, 2023
15b2386
add arb-gateway
AlexNi245 Nov 17, 2023
7ebb0c9
clean up workspace
AlexNi245 Nov 17, 2023
25b3c0e
fix workspace
AlexNi245 Nov 18, 2023
3f7d601
stash outbox
AlexNi245 Nov 18, 2023
0c31ad2
get rid of logging
AlexNi245 Nov 18, 2023
5e1ef4b
merkle tree wip
AlexNi245 Nov 18, 2023
2ab3282
checkpoint statevaldation
AlexNi245 Nov 18, 2023
ffb66d0
checkpoint finish contract + gateway
AlexNi245 Nov 18, 2023
57fd4b0
test for latest
AlexNi245 Nov 18, 2023
b925d95
finish test for latest
AlexNi245 Nov 18, 2023
16315dc
add test for name
AlexNi245 Nov 18, 2023
750fbc7
more tests
AlexNi245 Nov 18, 2023
c6159df
comments
AlexNi245 Nov 18, 2023
13da401
add comments
AlexNi245 Nov 18, 2023
c412d33
remove OpVerifier from arb-verifier dir
AlexNi245 Nov 19, 2023
82bbb12
add global run script
AlexNi245 Nov 19, 2023
971f3a2
update env in server
AlexNi245 Nov 19, 2023
5450c6a
renmae deploy script
AlexNi245 Nov 19, 2023
98b4174
replace outbox with rollup
AlexNi245 Nov 19, 2023
a6128cd
clean up code
AlexNi245 Nov 21, 2023
b54dfe9
test against nitro test node
AlexNi245 Nov 21, 2023
148255a
add comments
AlexNi245 Nov 21, 2023
75e4609
remove old v5 dep
AlexNi245 Nov 29, 2023
e7a9fa5
remove logs
AlexNi245 Nov 29, 2023
0d07456
change package order in global package.json
AlexNi245 Nov 29, 2023
5a60366
add instructions to run the tests for arb verifier
AlexNi245 Nov 30, 2023
50e9cc5
remove old hackathon test-script
AlexNi245 Nov 30, 2023
94c3d78
remove outdated comment
AlexNi245 Nov 30, 2023
4e86131
remove helperAbi since its no longer needed
AlexNi245 Nov 30, 2023
c1b2b05
revert liniting in other packages
AlexNi245 Nov 30, 2023
8108904
lint arb packages
AlexNi245 Nov 30, 2023
661853f
add comment reagarding the stateRoot
AlexNi245 Nov 30, 2023
db012b6
use correct chainId for ArbitrumGoerli
AlexNi245 Nov 30, 2023
ece9823
update README
AlexNi245 Dec 1, 2023
6865e7b
remove op contracts from arb-verifier package.json
AlexNi245 Dec 1, 2023
ae74902
Add goerli deployment info
makoto Dec 1, 2023
77a6b7c
Change worker name
makoto Dec 1, 2023
2c61006
Add comma
makoto Dec 1, 2023
479c4f8
Change to single quote
makoto Dec 1, 2023
d4a53e2
single quote
makoto Dec 1, 2023
b48975f
single quote
makoto Dec 1, 2023
896a818
comma
makoto Dec 1, 2023
519c861
comma
makoto Dec 1, 2023
381345a
Update readme
makoto Dec 1, 2023
be7eee5
Resolve conflicts
makoto Dec 1, 2023
4b7865c
Merge pull request #1 from ensdomains/add-arbi-validator-with-goerli-…
AlexNi245 Dec 1, 2023
b49dc6f
Support cors
makoto Dec 1, 2023
b43c139
Add preflight
makoto Dec 1, 2023
128bbbd
Revert "Add preflight"
makoto Dec 1, 2023
36f68d5
Revert "Support cors"
makoto Dec 1, 2023
c33121c
Update ccip-read-cf-worker to 0.0.2
makoto Dec 1, 2023
ef71297
Update ccip-read-cf-worker to v 0.0.3
makoto Dec 5, 2023
5cd3bbf
Merge pull request #2 from ensdomains/add-arbi-validator-with-goerli-…
AlexNi245 Dec 5, 2023
3d98ec7
Update README.md
makoto Dec 5, 2023
656e579
Merge pull request #3 from ensdomains/add-arbi-validator-with-goerli-…
AlexNi245 Dec 5, 2023
e9c065e
remove blank space
AlexNi245 Dec 6, 2023
2a1feb6
Merge remote-tracking branch 'origin/add-arbi-validator' into add-arb…
AlexNi245 Dec 6, 2023
91b3390
move rollup address to env
AlexNi245 Dec 6, 2023
cf6f9b1
commander extra typings is a dev dependency now
AlexNi245 Dec 6, 2023
d15a480
remove blockhash from calldata
AlexNi245 Dec 6, 2023
9770701
fix typo in ArbVerfifer
AlexNi245 Dec 6, 2023
d4d3147
add in memory cache
AlexNi245 Dec 8, 2023
402a151
rename cache to blockCache
AlexNi245 Dec 8, 2023
aac64fd
change method name to getL2BlockForNode
AlexNi245 Dec 8, 2023
74785ae
adjust console.log statements from InMemoryBlockCache
AlexNi245 Dec 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ This repository implements a generic CCIP-Read gateway framework for fetching st
While this functionality is written primarily with read calls in mind, it also functions for transactions; using a compliant
library like Ethers, a transaction that includes relevant L2 proofs can be generated and signed.



## Usage

1. Have your contract extend `EVMFetcher`.
Expand Down
21 changes: 21 additions & 0 deletions arb-gateway/LICENSE
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.
44 changes: 44 additions & 0 deletions arb-gateway/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# @ensdomains/arb-gateway

An instantiation of [evm-gateway](https://github.com/ensdomains/evmgateway/tree/main/evm-gateway) that targets Arbitrum - that is, it implements a CCIP-Read gateway that generates proofs of contract state on Arbitrum.

For a detailed readme and usage instructions, see the [monorepo readme](https://github.com/ensdomains/evmgateway/tree/main).

To get started, you need to have an RPC URL for both Ethereum Mainnet and Arbitrum. You also need to provide an L2_ROLLUP address which is the Rollup contract deployed on Mainnet or the Nitro Node.

## How to use arb-gateway locally via cloudflare dev env (aka wrangler)

```
npm install -g bun
cd arb-gateway
bun install
touch .dev.vars
## set L1_PROVIDER_URL, L2_PROVIDER_URL, L2_ROLLUP
AlexNi245 marked this conversation as resolved.
Show resolved Hide resolved
yarn dev
```

## How to deploy arb-gateway to cloudflare

```
cd arb-gateway
npm install -g wrangler
wrngler login

wrangler secret put L1_PROVIDER_URL
wrangler secret put L2_PROVIDER_URL
wrangler secret put L2_ROLLUP
yarn deploy
```

## How to test

1. Start the Nitro Test node. You can find instructions here: https://docs.arbitrum.io/node-running/how-tos/local-dev-node
2. Retrieve the Rollup address from the Node's Logs.
3. Copy the example.env file in both arb-gateway and arb-verifier, and add the Rollup address.
4. Build the Project.
5. Navigate to the Gateway directory using `cd ./arb-gateway`.
6. Start the Gateway by running `bun run start -u http://127.0.0.1:8545/ -v http://127.0.0.1:8547/ -p 8089`.
7. Open another Terminal Tab and navigate to the verifier directory using `cd ./arb-verifier/`.
8. Deploy contracts to the node using the command ` npx hardhat --network arbDevnetL2 deploy && npx hardhat --network arbDevnetL1 deploy `.
9. Run the test using the command `bun run test`.

Empty file added arb-gateway/example.env
Empty file.
70 changes: 70 additions & 0 deletions arb-gateway/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"name": "@ensdomains/arb-gateway",
"version": "0.1.0",
"author": "Nick Johnson",
"license": "MIT",
"type": "module",
"main": "./_cjs/index.js",
"module": "./_esm/index.js",
"types": "./_types/index.d.ts",
"typings": "./_types/index.d.ts",
"bin": "./_cjs/server.js",
"sideEffects": false,
"files": [
"_esm",
"_cjs",
"_types",
"src",
"!**/*.tsbuildinfo"
],
"exports": {
".": {
"types": "./_types/index.d.ts",
"import": "./_esm/index.js",
"require": "./_cjs/index.js"
},
"./package.json": "./package.json"
},
"engines": {
"node": ">=10",
"bun": ">=1.0.4"
},
"peerDependencies": {
"typescript": ">=5.0.4"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
},
"scripts": {
"start": "bun ./src/server.ts",
"dev": "wrangler dev",
"deploy": "wrangler deploy",
"build:cjs": "tsc --project tsconfig.build.json --module commonjs --outDir ./_cjs --removeComments --verbatimModuleSyntax false && echo > ./_cjs/package.json '{\"type\":\"commonjs\"}'",
"build:esm": "tsc --project tsconfig.build.json --module es2022 --outDir ./_esm && echo > ./_esm/package.json '{\"type\":\"module\",\"sideEffects\":false}'",
"build:types": "tsc --project ./tsconfig.build.json --module esnext --declarationDir ./_types --emitDeclarationOnly --declaration --declarationMap",
"build": "echo 'building arb-gateway...' && bun run clean && bun run build:cjs && bun run build:esm && bun run build:types",
"prepublishOnly": "bun ../scripts/prepublishOnly.ts",
"lint": "eslint . --ext .ts",
"prepare": "bun run build",
"clean": "rm -fr _cjs _esm _types"
},
"husky": {
"hooks": {
"pre-commit": "bun run lint"
}
},
"dependencies": {
"@chainlink/ccip-read-server": "^0.2.1",
"@ensdomains/evm-gateway": "^0.1.0",
"@ethereumjs/block": "^5.0.0",
"@nomicfoundation/ethereumjs-block": "^5.0.2",
"commander": "^11.0.0",
"ethers": "^6.7.1"
},
"devDependencies": {
"@commander-js/extra-typings": "^11.0.0"

}
}
160 changes: 160 additions & 0 deletions arb-gateway/src/ArbProofService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/* eslint-disable prettier/prettier */
import { EVMProofHelper, type IProofService } from '@ensdomains/evm-gateway';
import { AbiCoder, Contract, EventLog, ethers, toBeHex, type AddressLike, toNumber } from 'ethers';
import rollupAbi from "./abi/rollupABI.js";
import type { IBlockCache } from './blockCache/IBlockCache.js';
export interface ArbProvableBlock {
number: number
sendRoot: string,
nodeIndex: string,
rlpEncodedBlock: string
}


/**
* The proofService class can be used to calculate proofs for a given target and slot on the Arbitrum network.
* It's also capable of proofing long types such as mappings or string by using all included slots in the proof.
*
*/
export class ArbProofService implements IProofService<ArbProvableBlock> {
private readonly l2Provider: ethers.JsonRpcProvider;
private readonly rollup: Contract;
private readonly helper: EVMProofHelper;
private readonly cache: IBlockCache;


constructor(
l1Provider: ethers.JsonRpcProvider,
l2Provider: ethers.JsonRpcProvider,
l2RollupAddress: string,
cache: IBlockCache

) {
this.l2Provider = l2Provider;
this.rollup = new Contract(
l2RollupAddress,
rollupAbi,
l1Provider
);
this.helper = new EVMProofHelper(l2Provider);
this.cache = cache
}

async getStorageAt(block: ArbProvableBlock, address: AddressLike, slot: bigint): Promise<string> {
return this.helper.getStorageAt(block.number, address, slot);
}


/**
* @dev Fetches a set of proofs for the requested state slots.
* @param block A `ProvableBlock` returned by `getProvableBlock`.
* @param address The address of the contract to fetch data from.
* @param slots An array of slots to fetch data for.
* @returns A proof of the given slots, encoded in a manner that this service's
* corresponding decoding library will understand.
*/
async getProofs(
block: ArbProvableBlock,
address: AddressLike,
slots: bigint[]
): Promise<string> {
const proof = await this.helper.getProofs(block.number, address, slots);

return AbiCoder.defaultAbiCoder().encode(
[
'tuple(bytes32 version, bytes32 sendRoot, uint64 nodeIndex,bytes rlpEncodedBlock)',
'tuple(bytes[] stateTrieWitness, bytes[][] storageProofs)',
],
[
{
version:
'0x0000000000000000000000000000000000000000000000000000000000000000',
sendRoot: block.sendRoot,
nodeIndex: block.nodeIndex,
rlpEncodedBlock: block.rlpEncodedBlock
},
proof,
]
);
}
/**
* Retrieves information about the latest provable block in the Arbitrum Rollup.
*
* @returns { Promise<ArbProvableBlock> } A promise that resolves to an object containing information about the provable block.
* @throws Throws an error if any of the underlying operations fail.
*
* @typedef { Object } ArbProvableBlock
* @property { string } rlpEncodedBlock - The RLP - encoded block information.
* @property { string } sendRoot - The send root of the provable block.
* @property { string } blockHash - The hash of the provable block.
* @property { number } nodeIndex - The index of the node corresponding to the provable block.
* @property { number } number - The block number of the provable block.
*/
public async getProvableBlock(): Promise<ArbProvableBlock> {
//Retrieve the latest pending node that has been committed to the rollup.
const nodeIndex = await this.rollup.latestNodeCreated()
const [l2blockRaw, sendRoot] = await this.getL2BlockForNode(nodeIndex)

const blockarray = [
l2blockRaw.parentHash,
l2blockRaw.sha3Uncles,
l2blockRaw.miner,
l2blockRaw.stateRoot,
l2blockRaw.transactionsRoot,
l2blockRaw.receiptsRoot,
l2blockRaw.logsBloom,
toBeHex(l2blockRaw.difficulty),
toBeHex(l2blockRaw.number),
toBeHex(l2blockRaw.gasLimit),
toBeHex(l2blockRaw.gasUsed),
toBeHex(l2blockRaw.timestamp),
l2blockRaw.extraData,
l2blockRaw.mixHash,
l2blockRaw.nonce,
toBeHex(l2blockRaw.baseFeePerGas)
]

//Rlp encode the block to pass it as an argument
const rlpEncodedBlock = ethers.encodeRlp(blockarray)

return {
rlpEncodedBlock,
sendRoot,
nodeIndex: nodeIndex,
number: toNumber(l2blockRaw.number)
}
}
/**
* Fetches the corrospending L2 block for a given node index and returns it along with the send root.
* @param {bigint} nodeIndex - The index of the node for which to fetch the block.
* @returns {Promise<[Record<string, string>, string]>} A promise that resolves to a tuple containing the fetched block and the send root.
*/
private async getL2BlockForNode(nodeIndex: bigint): Promise<[Record<string, string>, string]> {

//We first check if we have the block cached
const cachedBlock = await this.cache.getBlock(nodeIndex)
if (cachedBlock) {
return [cachedBlock.block, cachedBlock.sendRoot]
}

//We fetch the node created event for the node index we just retrieved.
const nodeEventFilter = await this.rollup.filters.NodeCreated(nodeIndex);
const nodeEvents = await this.rollup.queryFilter(nodeEventFilter);
const assertion = (nodeEvents[0] as EventLog).args!.assertion
//Instead of using the AssertionHelper contract we can extract sendRoot from the assertion. Avoiding the deployment of the AssertionHelper contract and an additional RPC call.
const [blockHash, sendRoot] = assertion[1][0][0]


//The L1 rollup only provides us with the block hash. In order to ensure that the stateRoot we're using for the proof is indeed part of the block, we need to fetch the block. And provide it to the proof.
const l2blockRaw = await this.l2Provider.send('eth_getBlockByHash', [
blockHash,
false
]);

//Cache the block for future use
await this.cache.setBlock(nodeIndex, l2blockRaw, sendRoot)

return [l2blockRaw, sendRoot]
}

}