LapTrinhBlockchain

Chia sẻ kiến thức về Lập Trình Blockchain

Kiến thức Blockchain, Kiến thức phần mềm, Nâng cao Kiến thức

Hướng dẫn chạy fullnode trên Arbitrum sử dụng EC2 của Amazon

Hướng dẫn chạy fullnode trên Arbitrum sử dụng EC2 của Amazon

Hướng dẫn chạy fullnode trên Arbitrum sử dụng EC2 của Amazon

Chia sẻ bài viết
5
(8)

Đầu tiên để chạy fullnode bạn cần có 1 server trước đã. Mình sử dụng dịch vụ EC2 của Amazon để tạo.

Nếu bạn muốn sửa code thì xem hướng dẫn: How to build Nitro locally

Cài đặt fullnode trên Arbitrum

Có hai cách để chạy fullnode tương ứng 2 hướng dẫn mà mình tìm thấy trên mạng. Nhưng thực sự hơi lại cả hai cách này, lần đầu theo hướng dẫn của họ đều báo cùng lỗi:

arbitrum-node-nitro-node-1  | Version: v2.0.11-8e786ec, time: 2023-02-07T11:11:46-06:00
arbitrum-node-nitro-node-1  | Sample usage: /usr/local/bin/nitro --help
arbitrum-node-nitro-node-1  |
arbitrum-node-nitro-node-1  |
arbitrum-node-nitro-node-1  | Fatal configuration error: unable to create chain directory: mkdir /home/user/.arbitrum/arb1: permission denied
arbitrum-node-nitro-node-1  | Version: v2.0.11-8e786ec, time: 2023-02-07T11:11:46-06:00
arbitrum-node-nitro-node-1  | Sample usage: /usr/local/bin/nitro --help
arbitrum-node-nitro-node-1  |
arbitrum-node-nitro-node-1  |
arbitrum-node-nitro-node-1  | Fatal configuration error: unable to create chain directory: mkdir /home/user/.arbitrum/arb1: permission denied
arbitrum-node-nitro-node-1  | Version: v2.0.11-8e786ec, time: 2023-02-07T11:11:46-06:00
arbitrum-node-nitro-node-1  | Sample usage: /usr/local/bin/nitro --help
arbitrum-node-nitro-node-1  |
arbitrum-node-nitro-node-1  |
arbitrum-node-nitro-node-1  | Fatal configuration error: unable to create chain directory: mkdir /home/user/.arbitrum/arb1: permission denied
arbitrum-node-nitro-node-1  | Version: v2.0.11-8e786ec, time: 2023-02-07T11:11:46-06:00
arbitrum-node-nitro-node-1  | Sample usage: /usr/local/bin/nitro --help
arbitrum-node-nitro-node-1  |
arbitrum-node-nitro-node-1  |
arbitrum-node-nitro-node-1  | Fatal configuration error: unable to create chain directory: mkdir /home/user/.arbitrum/arb1: permission denied
arbitrum-node-nitro-node-1  | Version: v2.0.11-8e786ec, time: 2023-02-07T11:11:46-06:00
arbitrum-node-nitro-node-1  | Sample usage: /usr/local/bin/nitro --help

Đúng ra dữ liệu phải để ở thư mục của user hiện tại là /home/ubuntu, nhưng thực tế dữ liệu lại để ở thư mục /home/user/.arbitrum/arb1, có vẻ như Dev đang fix cứng thư mục này hoặc nhầm biến $user và xâu “user“. Vì vậy để quá trình diễn ra suôn sẻ bạn cần làm bước CHUẨN BỊ này trước:

// Chuyển sang tài khoản root
sudo -i

// Tạo thư mục
mkdir /home/user
mkdir /home/user/.arbitrum
mkdir /home/user/.arbitrum/arb1
chmod -fR 777 /home/user/.arbitrum

// Chuyển sang user thường
exit

Thêm nữa bạn cần link RPC của Ethereum nhé. Bạn có thể dùng node public hoặc từ dịch vụ từ các bên như Infura, Alchemy,… Ở đây để tiện chia sẻ tôi sử dụng public node, bạn có thể đổi sang RPC khác không ảnh hưởng gì nhé, chọn cái nào càng nhanh càng tốt:

https://ethereum.publicnode.com

Trong hai cách chạy node như ở dưới, bạn nên sử dụng C2 thì hợp lý hơn và dễ quản lý hơn sau này.

C1: Theo hướng dẫn sử dụng docker từ bên Arbitrum

Chi tiết hướng dẫn bạn xem tại: How to run a full node (Nitro). Nhưng thực sự nó không có cách lệnh theo từng bước nên cũng hơi khó dùng và phải mò mẫm thêm.

Các bạn nhớ làm bước CHUẨN BỊ ở trên trước nhé. Các lệnh để chạy node như sau:

// B1: Cài đặt docker
sudo apt update && sudo apt upgrade -y
sudo apt-get remove docker docker-engine docker.io containerd runc
sudo apt install curl -y
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

// B2: Chuyển sang account root
sudo -i

// B2: Pull snapshot về
docker pull offchainlabs/arb-node:v1.4.0-f4bbe91

// B3: chạy
docker run --rm -it -v /some/local/dir/arbitrum-mainnet/:/home/ubuntu/.arbitrum/mainnet -p 0.0.0.0:8547:8547 offchainlabs/arb-node:v1.4.0-f4bbe91 --l1.url https://ethereum.publicnode.com

C2: Theo hướng dẫn sử dụng docker-compose

Đây là hướng dẫn đầy đủ hơn từ Arbitrum One Node. Và tất nhiên bạn vẫn phải làm bước chuẩn bị ở trên.

// Cài đặt docker và docker-compose
sudo apt update && sudo apt upgrade -y
sudo apt-get remove docker docker-engine docker.io containerd runc
sudo apt install curl -y
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

// Xóa tệp cài đặt và cấp quyền
sudo rm -r get-docker.sh 
sudo usermod -aG docker ubuntu

// Kiểm tra cài đặt docker
docker --version
docker compose version

// Tạo thư mục
sudo mkdir /home/user
sudo mkdir /home/user/.arbitrum
sudo mkdir /home/user/.arbitrum/arb1
sudo chmod -fR 777 /home/user/.arbitrum
sudo chmod -fR 777 /home/user/.arbitrum/arb1
sudo mkdir -p /home/ubuntu/arbitrum-node/data
sudo chmod -fR 777 /home/ubuntu/arbitrum-node/data

// Tạo cấu hình cho docker-compose
// Nội dung file xem phần dưới
cd arbitrum-node
sudo nano docker-compose.yml

// Chuyển sang tài khoản root
sudo -i

// Chạy node
cd /home/ubuntu/arbitrum-node
docker compose up -d

// Check log
cd /home/ubuntu/arbitrum-node
docker compose logs -f nitro-node
docker compose logs -f nitro-node -n 100

// Tắt node
docker compose stop

// Bật lại node
docker compose start

// Xóa node
docker compose down

// Check dung lượng
du -sh /home/ubuntu/arbitrum-node/data
du -sh /home/user/.arbitrum

Nội dung tệp docker-compose.yml:

version: '3.3'
services:
    nitro-node:
        network_mode: host
        image: 'offchainlabs/nitro-node:v2.0.11-8e786ec'
        user: 1000:1000
        restart: always
        stop_grace_period: 30s
        volumes:
            - '/home/ubuntu/arbitrum-node/data/:/home/user/.arbitrum'
        ports:
            - '0.0.0.0:8547:8547'
            - '0.0.0.0:8548:8548'
        command:
        - --init.url=https://snapshot.arbitrum.foundation/arb1/nitro-pruned.tar
        - --l1.url=https://rpc.ankr.com/eth
        - --l2.chain-id=42161
        - --http.api=net,web3,eth,debug
        - --http.corsdomain=*
        - --http.addr=0.0.0.0
        - --http.vhosts=*
        logging:
          driver: json-file
          options:
            max-size: 10m
            max-file: "10"

Cấu hình này chỉ chạy được RPC, không có WebSocket. Sau đó cấu hình lại như dưới để hỗ trợ cả WebSocket và Feeder:

version: '3.3'
services:
    nitro-node:
        network_mode: host
        image: 'offchainlabs/nitro-node:v2.0.14-2baa834'
        user: 1000:1000
        restart: always
        stop_grace_period: 30s
        volumes:
            - '/home/ubuntu/arbitrum-node/data/:/home/user/.arbitrum'
        ports:
            - '0.0.0.0:8547:8547'
            - '0.0.0.0:8548:8548'
            - '0.0.0.0:9642:9642'
        command:
        - --init.url=https://snapshot.arbitrum.foundation/arb1/nitro-pruned.tar
        - --l1.url=https://rpc.ankr.com/eth
        - --l2.chain-id=42161
        - --http.api=net,web3,eth,debug
        - --http.corsdomain=*
        - --http.addr=0.0.0.0
        - --http.vhosts=*
        - --node.feed.output.addr=0.0.0.0
        - --ws.api=net,web3,eth,arb,debug
        - --ws.port=8548
        - --ws.addr=0.0.0.0
        - --ws.origins=*
        - --node.staker.enable=false
        - --node.transaction-streamer.execute-message-loop-delay=10ms
        logging:
          driver: json-file
          options:
            max-size: 10m
            max-file: "10"

Sau đó chạy lại bằng lệnh sau:

// Chuyển đến thư mục chứa tệp cấu hình
cd /home/ubuntu/arbitrum-node

// Xóa node cũ
docker compose down

// Chạy lại node
docker compose up -d

Đến đây thì RPC và Websocket thì đã OK nhưng Feeder vẫn không được. Cuối cùng với Feeder đành chạy lệnh dưới để tạo Feeder Relay riêng:

docker run --rm -it  -p 0.0.0.0:9642:9642 --entrypoint relay offchainlabs/nitro-node:v2.0.14-2baa834 --node.feed.output.addr=0.0.0.0 --node.feed.input.url=wss://arb1.arbitrum.io/feed --l2.chain-id=42161

Sau khi chạy xong bạn sẽ có:

// RPC
http://localhost:8547/
http://<IP>:8547/

// Websocket
ws://localhost:8548/
ws://<IP>:8548/

// Feeder
ws://localhost:9642/
ws://<IP>:9642/

Sử dụng fullnode

Một số thông tin khi chạy fullnode

Một số thông tin khi chạy node ngày 2023-07-25 mà tôi thống kê được:

  • Về chi phí: Tôi sử dụng EC2 của AWS rơi đâu đó tầm 227$, bạn chỉ cần sử dụng cấu hình 1 là được, tất nhiên quá trình khởi tạo ban đầu sẽ lâu hơn:
    • Cấu hình 1: Chi phí khoảng 227$ / tháng
      • t2.large (2 vCPU + 8G RAM)
      • SSD: 1600G
      • Server tại zone US East (N. Virginia) – us-east-1
      • Sử dụng thực tế:
        • Quá trình đồng bộ CPU luôn ở trạng thái 90% đến 95%
    • Cấu hình 2: Chi phí khoảng 280$ / tháng
      • t3.xlarge: 4 vCPU + 16G RAM
      • SSD: 1600G
      • Server tại zone US East (N. Virginia) – us-east-1
      • Sử dụng thực tế:
        • Quá trình đồng bộ CPU khoảng 15% đến 50%
        • Sau khi đồng bộ xong thì CPU dao động tầm 1% đến 3%,
  • Thông tin khởi tạo (Cấu hình 2):
  • Thông tin khi ở chế độ bình thường (Cấu hình 2):
    • CPU dao động từ 1% đến 3%RAM sử dụng khoảng 6GSSD 986G
    • Đo dữ liệu RPC từ server:
      • Response time:
        • 73.5 mili seconds (15s/request)
        • 40 mili seconds (Request liên tục) => Có thể do cơ chế cache
      • Block: 2.442308

Sử dụng RPC

Sau khi node chạy okie xong thì rpc của nó có dạng:

http://localhost:8547
http://<IP>:8547

Lệnh sau để kiểm tra RPC node:

// Kiểm tra thử 1 public rpc
curl --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST https://arbitrum-one.publicnode.com

// Lấy blocknumber mới nhất qua RPC
curl --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST http://localhost:8547

// Xem fullnode có phải vẫn đang đồng bộ không
curl --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' -H "Content-Type: application/json" -X POST http://localhost:8547

// Lấy thông tin block mới nhất
curl --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest", false],"id":1}' -H "Content-Type: application/json" -X POST http://localhost:8547

// Lấy chainId
curl --data '{"method":"eth_chainId","params":[],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST http://localhost:8547

// Lấy balance ETH
curl --data '{"method":"eth_getBalance","params":["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "latest"],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST http://localhost:8547

Chi tiết về các lệnh RPC, bạn có thể xem tại: Ethereum JSON-RPC-API

Hướng dẫn build Nitro từ mã nguồn

Nếu bạn muốn can thiệp sau hơn vào Nitro node thì bạn cần phải sửa mã nguồn, trước tiên bạn cần build để có thể chạy được trước đã.

Build trực tiếp theo hướng dẫn từ dự án

Hướng dẫn build xem: How to build Nitro locally (Docker, Debian). Tài liệu hướng dẫn này áp dụng cho Debian 11.7 (arm64)Ubuntu 22.04 (amd64)

Phải đúng môi trường mới build được, các bước khá phức tạp nên mình chưa thực hiện theo cách này.

Build sử dụng docker

// Tải mã nguồn trực tiếp từ tệp zip
// Nếu dùng git clone thì bạn nhớ git clone cả sub project về
wget https://github.com/OffchainLabs/nitro/archive/refs/tags/v2.0.14.zip
unzip v2.0.14.zip
cd nitro-2.0.14

// Cập nhật một số file thiếu, do sub-module nên ko có trong dữ liệu tải về
// Lấy từ https://github.com/OffchainLabs/nitro/tree/v2.0.14
// Lấy toàn bộ tệp trong thư mục blockscout, brotli, fastcache, go-ethereum về thư mục tương ứng

// Build sử dụng docker
sudo docker build -t nitro-node-arbi:1.0 .

Khi build mất tầm 15 phút đến 30 phút, sau khi build xong bạn được tệp image như hình:

Màn hình thông báo thành công sau khi build nitro node
Màn hình thông báo thành công sau khi build nitro node

Tìm hiểu sâu hơn mã nguồn của fullnode nitro

Mã nguồn fullnode nitro tại: https://github.com/OffchainLabs/nitro/tree/v2.0.14

Cơ chế đồng bộ của Fullnode

Arbitrum không có cơ chế đồng thuận, vì vậy “p2p mode” không sử dụng. Để các nút đồng bộ hóa với trạng thái chuỗi mới nhất, chúng kết nối với nút L1 để đồng bộ hóa lịch sử của chuỗi đã được đăng trong calldata và kết nối với nguồn cấp Sequencer cho các giao dịch chưa được đăng theo lô. Chi tiết xem: Can I run an Arbitrum node in p2p mode?The Sequencer and Censorship Resistance

Như vậy các node đồng bộ hóa thông qua L1 RPCSequencer. Về dữ liệu L1 vài phút mới cần cập nhật 1 lần, vì vậy xử lý dữ liệu từ Sequencer quan trọng, ảnh hưởng tới tốc độ đồng bộ dữ liệu từ server. Để lấy dữ liệu từ Sequencer chúng ta cần kết nối tới một Sequencer Feeder và cách đọc xem bài: How to read the Sequencer feed. Sequencer Feeder:

Sequencer là tập trung, làm nhiệm vụ sắp xếp các giao dịch, thông thường giao dịch nào đến trước sẽ được xử lý trước. Các giao dịch này đồng thời public tới các node để các node đồng bộ và tính toán trạng thái mới nhất của blockchain. Sau một khoảng thời gian (Khoảng vài phút), Sequencer cũng sẽ đẩy 1 lô các giao dịch L2 sang L1. Các node làm Staker sẽ có nhiệm vụ xác thực các giao dịch trên L1.

Vòng đời một giao dịch trên Arbitrum

Tổng quan vòng đời giao dịch trên Arbitrum xem: Overview: The Lifecycle of an Arbitrum Transaction

Tìm hiểu về luồng nhận message từ feeder và cập nhật trạng thái mới nhất

Theo như tìm hiểu được, Fullnode sẽ kết nối tới Sequencer (wss://arb1.arbitrum.io/feed) để lấy thông tin các giao dịch trên L2, từ đó Fullnode sẽ tính toán để ra trạng thái mới nhất của blockchain.

Chi tiết code:

  1. Bắt đầu từ hàm Start() in arbnode\node.go => Gọi tới hàm n.TxStreamer.Start(ctx)
  2. Hàm Start() in arc node\transaction_streamer.go => Gọi hàm executeMessages()
  3. Hàm executeMessages() in arc node\transaction_streamer.go => Call executeNextMsg()
    Trả về ExecuteMessageLoopDelay, mặc định tham số này 100ms. Tôi tìm thấy cấu hình này trong Command-line options:
    –node.transaction-streamer.execute-message-loop-delay duration delay when polling calls to execute messages (default 100ms)
  4. Hàm executeNextMsg() in arbnode\transaction_streamer.go => Gọi hàm s.exec.DigestMessage(pos, msg)
  5. Hàm DigestMessage() in arbnode/execution/executionengine.go => Gọi hàm s.digestMessageWithBlockMutex(num, msg)
  6. Hàm digestMessageWithBlockMutex() in arbnode/execution/executionengine.go => Hàm xử lý chính để tạo block.

Như vậy theo như luồng code thì Fullnode sẽ nhận liên tục các message từ Sequencer, với mỗi message nhận được, fullnode sẽ xử lý để tạo ra block mới. Vòng lặp này thực hiện 100ms một lần, muốn đẩy nhanh ta giảm thời gian vòng lặp này xuống bằng cách sửa tham số:

--node.transaction-streamer.execute-message-loop-delay=10ms

Thêm log vào source code

Do yêu cầu cần đo thời gian một số sự kiện trên Fullnode, cụ thể:

  • (1) Thời gian fullnode nhận data từ Feeder
  • (2) Thời gian fullnode hoàn thành xử lý block mới

Có hỏi bên OffchainLabs nó báo, cái (1) có thể thêm log ở broadcastclient.go#L376, còn cái (2) nó đã có sẵn ở executionengine.go#L603. Nhưng xem code cái (2) thì thấy nó là thread khác để ghi log, ghi log 1s 1 lần, chứ không phải đúng là thời gian hoàn thành xử lý block mới.

Theo như code thì bắt đầu xử lý block ở executionengine.go#L516 và kết thúc xử lý block ở executionengine.go#L529 => Đây là chỗ cần thêm log cho thằng (2)

Luồng bắn các log events cho client

Phần xử lý sinh ra các events nằm blockchain.go#L1445

Bài viết này có hữu ích với bạn?

Kích vào một biểu tượng ngôi sao để đánh giá bài viết!

Xếp hạng trung bình 5 / 5. Số phiếu: 8

Bài viết chưa có đánh giá! Hãy là người đầu tiên đánh giá bài viết này.

Trả lời

Giao diện bởi Anders Norén