note: มาลองรัน L2 chain ด้วย OP Stack กัน
มา walkthrough รวดเดียวให้จบไปเลยกัน
note: ผมใช้ ubuntu 20 นะครับ
เริ่มจาก sudo apt-get update
จากนั้นลง software ที่ต้องใช้ตามภาพ
note: เหมือนตอน sudo apt-get install -y nodejs npm เค้าจะใส่คำสั่งมาเกินเลย error หะ จริงๆ sudo apt-get install -y nodejs ก็พอ
note: ต้องลง yarn ก่อนไป install foundry ซึ่งทำได้ด้วยคำสั่ง
## To install the Yarn package manager, run:
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/yarnkey.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn
note: คำสั่ง yarn install:foundry เป็นการ install แบบ local ใน folder ที่มี package.json แต่เราจะ install แบบ global โดยใช้คำสั่งด้านล่างแทน
curl -L https://foundry.paradigm.xyz | bash
source /root/.bashrc
foundryup
โอเค ต่อมาเราจะลง component ต่างๆของ optimism chain ลงในเครื่องโดยคำสั่ง
cd ~
git clone https://github.com/ethereum-optimism/optimism.git
cd optimism
pnpm install
make op-node op-batcher op-proposer
pnpm build
ต่อมาก็ลง op-geth
cd ~
git clone https://github.com/ethereum-optimism/op-geth.git
cd op-geth
make geth
โอเค ต่อมาจะเป็นการเตรียม account สำหรับ component ต่างๆของ chain L2 โดยประกอบด้วย
- admin account สำหรับไว้ upgreade contract (upgrade ยังไงหว่า เดี๋ยวเจอแล้วจะมาแปะอีกที)
- batcher account สำหรับ publish transaction data จาก sequencer ไปยัง L1
- proposer account สำหรับ publish trasaction result ของ L2 ไปยัง L1
- sequencer account สำหรับการ sign block บน p2p network
โดยในการ gen account สามารถใช้ rekey ที่ติดมากับ optimisim monorepo ที่เรา clone มาแล้วได้เลยด้วยคำสั่ง
cd ~
cd packages/contracts-bedrock
echo "Admin:"
cast wallet new
echo "Proposer:"
cast wallet new
echo "Batcher:"
cast wallet new
echo "Sequencer:"
cast wallet new
จะได้ private key มาประมาณนี้
Admin:
Successfully created new keypair.
Address: 0x9f92bdF0db69264462FC305913960Edfcc7a7c7F
Private key: 0x30e66956e1a12b81f0f2cfb982286b2f566eb73649833831d9f80b12f8fa183c
Proposer:
Successfully created new keypair.
Address: 0x31dE9B6473fc47af36ec23878bA34824B9F4AB30
Private key: 0x8bd1c8dfffef880f8f9ab8162f97ccd119c1aac28fe00dacf919459f88e0f37d
Batcher:
Successfully created new keypair.
Address: 0x6A3DC843843139f17Fcf04C057bb536A421DC9c6
Private key: 0x3ce44144b7fde797a28f4e47b210a4d42c3a3b642e538b54458cba2740db5ac2
Sequencer:
Successfully created new keypair.
Address: 0x98C6cadB1fe77aBB7bD968fC3E9b206111e72848
Private key: 0x3f4241229bb6f155140d98e0f5dd2aad7ae983f5af5d61555d05eb8e5d9514db
โอเค จริงๆใน tutorial ต้นฉบับ เป็นการรัน L2 chain บน Goerli L1 chain
โดยต้องใส่ GETH ให้แต่ละ account ดังนี้
Admin — 2 ETH
Proposer — 5 ETH
Batcher — 10 ETH
ซึ่งใช้เยอะแบบนี้คงต้องไปซื้อเอา
ดังนั้นแทนที่จะใช้ Goerli ่เราจะลองใช้ local chain ของเราเอง เพื่อให้เราสามารถเสก ETH ได้เองกัน
โดยเนื่องจากเราลง foundry ในเครื่องไปแล้ว ดังนั้นเราจะใช้ anvil ใน foundry เพื่อเปิด chain เองกัน โดยรันคำสั่ง
anvil
จะได้ result แบบนี้ ให้เปิดค้างไว้
โดยเราจะใช้ account พวกนี้แทนพวก admin batcher … ที่เรา gen ไปก่อนหน้าเลยแล้วกัน
โอเค ต่อมาจะเป็นการ config ตัว network L2 ของเรา โดยเราต้องเข้าไปที่ folder ที่เก็บ example config ก่อน
cd ~/optimism
cd packages/contracts-bedrock
สร้างไฟล์ config จาก eaxmple
cp .envrc.example .envrc
แก้ไฟล์ config
nano .envrc
โดยแก้ตามนี้
ETH_RPC_URL แก้ให้ชี้ไป RPC ของ anvil ซึ่งคือ http://127.0.0.1:8545/
PRIVATE_KEY ใส่ private key ของ accout admin ไป
DEPLOYMENT_CONTEXT อันนี้มาตั้งชื่อ chain เป็น getting-started กัน
โอเค ต่อมาเป็นการสั่งให้ chain ใช้ config นี้ โดยเราต้องลง direnv ก่อน
apt install direnv
จากนั้นให้เรา hook ตัว direnv เข้ากับ bash shell ด้วยการแก้ไฟล์ ~/.bashrc
nano ~/.bashrc
โดยเติม eval "$(direnv hook bash)" ไปที่บรรทัดล่างสุด แล้ว เปิด terminal ใหม่
โอเค กลับไปที่ folder ไฟล์ config network
cd ~/optimism
cd packages/contracts-bedrock
note: ต้องใช้ user เดียวกับที่ทำใน terminal แรกแหะ ใน case นี้เป็น root
note: จะขึ้น error นี้มา
ให้รันคำสั่ง
direnv allow .
ตอนนี้พอเราออกไปที่อื่น แล้วเข้ามาที่ folder นี้ใหม่ก็จะไม่ขึ้น error แล้ว
โอเค ต่อมาเราจะต้องกำหนดเลข block ที่ L2 จะเริ่ม
โดยเราจะใช้ finalized block ของ anvil ละกัน
โดยเราสามารหาเลข block นั้นได้ด้วยคำสั่ง
cast block finalized --rpc-url http://localhost:8545 | grep -E "(timestamp|hash|number)"
จะได้ result แบบนี้
hash 0xb17ac070f0565f4ec886c34a7299833547c4c34f05b78326a99f88440887c1ba
number 0
timestamp 1691220541
โอเค มาแก้ config ของ chain ในไฟล์ deploy-config/getting-started.json กัน
nano deploy-config/getting-started.json
โดยหา value ที่เป็นคำเหล่านี้ ADMIN, PROPOSER, BATCHER, SEQUENCER
แล้ว replace ด้วย address ของ wallet ต่างๆที่เราเตรียมไว้
และแก้ BLOCKHASH และ TIMESTAMP เป็นค่าของ finalized block ที่เราได้มา
จากนั้นเราจะมา deloy contract บน L1 ที่ต้องใช้กัน
โดยเริ่มจากสร้าง folder
mkdir deployments/getting-started
จากนั้น deploy contract ด้วย
forge script scripts/Deploy.s.sol:Deploy --private-key $PRIVATE_KEY --broadcast --rpc-url $ETH_RPC_URL
forge script scripts/Deploy.s.sol:Deploy --sig 'sync()' --private-key $PRIVATE_KEY --broadcast --rpc-url $ETH_RPC_URL
โดยใช้ private key ของ ADMIN และ rpc url ของ anvil
โอเค setup L1 ไปละ มา setup L2 กันต่อ
cd ~/optimism/op-node
go run cmd/main.go genesis l2 --deploy-config ../packages/contracts-bedrock/deploy-config/getting-started.json --deployment-dir ../packages/contracts-bedrock/deployments/getting-started/ --outfile.l2 genesis.json --outfile.rollup rollup.json --l1-rpc http://localhost:8545
แต่เจอ error แหะ
เป็นเพราะ block ของ anvil ไม่ได้ถูกขุดไปเรื่อยๆรึเปล่า ?
งั้นมาเปิด chain anvil ใหม่ แบบให้ block ถูกขุดทุก 10 วินาทีแล้วกัน
# Produces a new block every 10 seconds
anvil --block-time 10
เอาละ มาหา finalized block ของ chain ใหม่นี้กัน
cast block finalized --rpc-url http://localhost:8545 | grep -E "(timestamp|hash|number)"
จะได้
hash 0xf5bd890185c6fbb8d386b54a54719dc6910138afefbd3fb4e4f2fa228bb84e5d
number 0
timestamp 1691225369
โอเค ต้องมาแก้ config ของ L1 กันใหม่
nano ~/optimism/packages/contracts-bedrock/deploy-config/getting-started.json
แก้ l1StartingBlockTag เป็น blockhash ของ finalized block
แก้ l2OutputOracleStartingTimestamp เป็น timestamp ของ finalized block
โอเค deploy contract ที่จะเป็นต้องใช้บน L1 กันใหม่
cd ~/optimism/packages/contracts-bedrock
forge script scripts/Deploy.s.sol:Deploy --private-key $PRIVATE_KEY --broadcast --rpc-url $ETH_RPC_URL
forge script scripts/Deploy.s.sol:Deploy --sig 'sync()' --private-key $PRIVATE_KEY --broadcast --rpc-url $ETH_RPC_URL
โอเค มาสร้างไฟล์ genesis.json กับ rollup.json ของ L2 กันใหม่กัน
cd ~/optimism/op-node
go run cmd/main.go genesis l2 --deploy-config ../packages/contracts-bedrock/deploy-config/getting-started.json --deployment-dir ../packages/contracts-bedrock/deployments/getting-started/ --outfile.l2 genesis.json --outfile.rollup rollup.json --l1-rpc http://localhost:8545
เอ้า ยัง error เหมือนเดิมเลยแหะ
กำ พออ่าน error ดูดีๆแล้ว เหมือนว่าจะเป็นเพราะ value l2BlockTime เป็น 2 ซึ่งเยอะกว่า 0 แหะ
เอาจริงๆ เหมือน file getting-started.json นี้จะทำมาเพื่อเชื่อม goerli เลย งั้นมาแก้ l1ChianID เป็น 31337 กัน
error ยังไม่หายแหะ
ตัว OP stack ไปถึง L1 blocktime เป็น 0 มาได้ยังไงหว่า
โอเค ไม่เป็นไร งั้นลองมา fork goerli ด้วย anvil แทนแล้วกัน
โอเค เดี๋ยวเราเติม — blocktime เป็น 12 วิไปด้วย
โอเค มารันคำสั่งกันใหม่อีกรอบดู
พอ deploy contract แล้ว error แหะ
พอเข้าไปดูไฟล์ .chainId ตาม error ก็เจอว่ายังเป็นค่าเก่าแหะ
ไม่รู้ว่าไฟล์นี้ถูกสร้างตอนไหนแหะ
งั้นแก้ค่าเป็น 5 ไปตรงๆเลยแล้วกัน
ทำยังไงก็ error แหะ
งั้นลบ folder สร้างใหม่ไปเลยแล้วกัน
cd ~/optimism/packages/contracts-bedrock
rm -rf deployments/getting-started
mkdir deployments/getting-started
forge script scripts/Deploy.s.sol:Deploy --private-key $PRIVATE_KEY --broadcast --rpc-url $ETH_RPC_URL
forge script scripts/Deploy.s.sol:Deploy --sig 'sync()' --private-key $PRIVATE_KEY --broadcast --rpc-url $ETH_RPC_URL
cd ~/optimism/op-node
go run cmd/main.go genesis l2 --deploy-config ../packages/contracts-bedrock/deploy-config/getting-started.json --deployment-dir ../packages/contracts-bedrock/deployments/getting-started/ --outfile.l2 genesis.json --outfile.rollup rollup.json --l1-rpc http://localhost:8545
โอเค กลับมาที่ error blocktime l1 = 0 แหะ
โอเค งั้นลองหา code ที่ print error นี้มากัน
อืม หาไม่เจอแหะว่าค่า l1BlockTime มาจากไหน 555
ค้างไว้ตรงนี้ก่อนแล้วกัน