This repository contains smart contracts and a node.js gateway server that together allow storing ENS names on Arbitrum using EIP 3668 and ENSIP 10.
git clone https://github.com/offchainlabs/nitro
cd nitro
git submodule update --init --recursive
./test-node.bash --no-blockscout --init
cd packages/contracts
npx hardhat --network arbitrumLocalhost run scripts/deployL2.js
// Take notes of L2_RESOLVER_ADDRESS
RESOLVER_ADDRESS=$L2_RESOLVER_ADDRESS npx hardhat --network localhost run scripts/deployL1.js
// Take notes of $ENS_REGISTRY_ADDRESS
// Take notes of $HELPER_ADDRESS
cd packages/gateway
yarn build
yarn start --l2_resolver_address $L2_RESOLVER_ADDRESS --helper_address $HELPER_ADDRESS
cd packages/clients
yarn start -r $ENS_REGISTRY_ADDRESS test.test
If sucessful, it should show the following output
options {
l1_provider_url: 'http://localhost:8545',
ensAddress: '0x3E9b8c645914626C2f304994d5992527c3ab528b',
chainId: 1337,
chainName: 'unknown',
debug: false
}
getAddress 0x683642c22feDE752415D4793832Ab75EFdF6223c
(call time= 189 )
getAddress(60) 0x683642c22feDE752415D4793832Ab75EFdF6223c
_fetchBytes 0x683642c22fede752415d4793832ab75efdf6223c
addr(bytes32) 0x683642c22feDE752415D4793832Ab75EFdF6223c
addr(bytes32,uint256) 0x683642c22fede752415d4793832ab75efdf6223c
resolveName 0x683642c22feDE752415D4793832Ab75EFdF6223c
✨ Done in 3.84s.
TEST_NAME=$TEST_NAME \
L1_PROVIDER_URL=$L1_PROVIDER_URL \
L2_PROVIDER_URL=$L2_PROVIDER_URL
PRIVATE_KEY=$PRIVATE_KEY \
npx hardhat --network arbitrumGoerli run scripts/deployL2.js
L1_PROVIDER_URL=L1_PROVIDER_URL \
L2_PROVIDER_URL=L2_PROVIDER_URL \
PRIVATE_KEY=PRIVATE_KEY \
RESOLVER_ADDRESS=RESOLVER_ADDRESS \
npx hardhat --network goerli run scripts/deployL1.js
RESOLVER_ADDRESS=
L1_PROVIDER_URL=
ETHERSCAN_API_KEY=
npx hardhat verify --network goerli --constructor-args scripts/arguments.js CONTRACT_ADDRESS
- Arbitrum goerli resolver = 0xE2ea775DfEb454F7E667e71029b401b6C08fF000
- goerli (gateway points to 'https://arb-resolver-example.uc.r.appspot.com/{sender}/{data}.json' ) = 0x142884839B7A5F952aD00bF65c5baf225257FFa2
- goerli test domain = arb-resolver.eth
Create secret.yaml and update credentials
cd gateway
cp secret.yaml.org secret.yaml
Deploy to app engine
gcloud app deploy goeril.app.yml
Every time l2 state is updated, the validator stores the l2 state information as Node
into Rolleup
L1 contract by sending the Assertion
(the claim about the current state) via Rollup.createNewNode
function. To verify the storage slot of l2, it requires the following 3 steps.
-
- Prove a certain l2 block hash correspond to a node/rblock(raw block) state with the assertion
-
- Prove the state root belong to the l2 block hash by supplying the blockheader
-
- Prove the storage root is part of the state root
The full reference code is at https://gist.github.com/gzeoneth/0a8bac381752e4b4f30650a0d3c76096
- Find the latest node index by calling
rollup.latestNodeCreated()
for all posted rblock data by a validator (rollup.latestNodeConfirmed()
for all confirmed data) - Fetch an assertion from the node event
- Fetch blockhash via
GlobalStateLib.getBlockHash(assertion.afterState.globalState);
- Fetch sendRoot via
GlobalStateLib.getSendRoot(assertion.afterState.globalState);
- Manually derive confirmData by hashing blockhash and sendRoot
- Fetch rblock via
await rollup.getNode(nodeIndex)
- Compare
confirmData == rblock.confirmData
2. Prove the state root belong to the l2 block hash by supplying the blockheader <= where do I find blockheader?
- Fetch l2 block (
l2blockRaw
) viaeth_getBlockByHash(blockhash)
- Manually derive blockhash by hashing block header fields
- Compare the derived value against
proof.blockHash
This step is almost identical to how to verify it on Optimism and uses Optimism MerkleTrie contract library.
- Proof the account storage inside the state root
- Proof the storage slot is in the account root
The state of an Arbitrum chain is confirmed back on Ethereum via "assertions," aka "disputable assertions" or "DAs." These are claims made by Arbitrum validators about the chain's state. To make an assertion, a validator must post a bond in Ether. The more on "The assertion tree" section.
The Assertion is represented in the following fields within Arbitrum Nitro.
struct Assertion {
ExecutionState beforeState;
ExecutionState afterState;
uint64 numBlocks;
}
struct ExecutionState {
GlobalState globalState;
MachineStatus machineStatus;
}
struct GlobalState {
bytes32[2] bytes32Vals; // (blockhash, sendRoot)
uint64[2] u64Vals; // (inboxPosition, positionInMessage)
}
Hash of the l2 block header. The block header is composed of 15 distinct fields as per the yellow paper.
confirmData is how Arbitrum stores the l2 state in rblock, contatination of blockHash and sendRoot (concatinated to save storage slot)
Slow Inbox is a sequence of L1 initiated message that offer an alternative path for inclusion into the fast Inbox. Fast Inbox is a Contract that holds a sequence of messages sent by clients to the contracts on an Arbitrum Chain; message can be put into the Inbox directly by the Sequencer or indirectly through the slow inbox.
An L1 contract responsible for tracking outgoing (Arbitrum to Ethereum) messages, including withdrawals, which can be executed by users once they are confirmed. The outbox stores a Merkle Root of all outgoing messages.
The rollup protocol tracks a chain of rollup blocks---we'll call these "RBlocks" for clarity. They're not the same as Layer 1 Ethereum blocks, and also not the same as Layer 2 Nitro blocks. You can think of the RBlocks as forming a separate chain, which the Arbitrum rollup protocol manages and oversees.
Validators can propose RBlocks. New RBlocks will be unresolved at first. Eventually every RBlock will be resolved, by being either confirmed or rejected. The confirmed RBlocks make up the confirmed history of the chain.
Each RBlock contains:
- the RBlock number
- the predecessor RBlock number: RBlock number of the last RBlock before this one that is (claimed to be) correct
- the number of L2 blocks that have been created in the chain's history
- the number of inbox messages have been consumed in the chain’s history
- a hash of the outputs produced over the chain’s history (= sendRoot)
RBlocks typically get asserted every 30-60 minutes. More detail in the doc
The root of the last confirmed transaction in Outbox. If the sendRoot information is in outbox, you can be assured that the stat is confirmed.
An entity (currently a single-party on Arbitrum One) given rights to reorder transactions in the Inbox over a small window of time, who can thus give clients sub-blocktime soft confirmations. (Not to be confused with a validator)