Skip to content

Commit

Permalink
Resolve conflicts and update bun
Browse files Browse the repository at this point in the history
  • Loading branch information
makoto committed Dec 10, 2023
2 parents d173654 + 8d5231b commit acabddc
Show file tree
Hide file tree
Showing 33 changed files with 4,852 additions and 3 deletions.
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
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]
}

}
Loading

0 comments on commit acabddc

Please sign in to comment.