LapTrinhBlockchain

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

Kiến thức Blockchain, Nâng cao Kiến thức

Hướng dẫn tạo các “Forking mainnet” để sử dụng trên local cho các mạng dạng EVM

Hướng dẫn tạo các “Forking mainnet” để sử dụng trên local cho các mạng dạng EVM

Hướng dẫn tạo các “Forking mainnet” để sử dụng trên local cho các mạng dạng EVM

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

Sử dụng Hardhat để tạo “Forking mainnet”

Hardhat Network có khả năng sao chép trạng thái của chuỗi khối mainnet vào môi trường cục bộ của bạn, bao gồm tất cả số dư và hợp đồng đã triển khai. Điều này được gọi là “forking mainnet“.

Trong môi trường cục bộ được phân nhánh từ mạng chính, bạn có thể thực hiện các giao dịch để gọi các hợp đồng được triển khai trên mạng chính hoặc tương tác với mạng theo bất kỳ cách nào khác mà bạn thực hiện với mạng chính. Ngoài ra, bạn có thể làm bất cứ điều gì được hỗ trợ bởi Mạng Hardhat không phân nhánh: xem nhật ký bảng điều khiển, lấy dấu vết ngăn xếp hoặc sử dụng tài khoản mặc định để triển khai hợp đồng mới.

Tổng quát hơn, Hardhat Network có thể được sử dụng để rẽ nhánh bất kỳ mạng nào, không chỉ mạng chính. Hơn nữa, Mạng Hardhat có thể được sử dụng để rẽ nhánh bất kỳ chuỗi khối tương thích với EVM nào , không chỉ Ethereum. Chi tiết bạn xem bài viết: Forking other networks

Các lệnh tạo “forking mainnet” cho Arbitrum (Yêu cầu NodeJs phải từ 16 trở lên):

// Cài đặt hardhat, yêu cầu phải cài hardhat 2.12 trở lên
// Xem https://docs.arbitrum.io/faqs/tooling-faqs#q-hardhat
npm install --save-dev "hardhat@2.17.1"
// Nếu không được thì chạy lệnh sau:
yarn add hardhat@2.17.1

// Forking ở block hiện tại
// Hiện dùng public node, bạn nên sử dụng node của Alchemy hoặc Infura
npx hardhat node --fork https://rpc.ankr.com/arbitrum
// Nếu chạy lần đầu có thể  hỏi thêm một số thứ, làm theo hướng dẫn là được

// Forking ở block xác định
npx hardhat node --fork https://rpc.ankr.com/arbitrum --fork-block-number 121570800

Bạn có thể chạy node thông qua cấu hình trong tệp hardhat.config.js với nội dung như sau:

module.exports = {
    networks: {
        hardhat: {
            forking: {
                url: "https://rpc.ankr.com/arbitrum",
                blockNumber: 121570800
            }
        }
    },
    solidity: "0.8.19",
};

Sau đó bạn đánh lệnh:

npx hardhat node

Sau khi node được chạy bạn sẽ nhận được:

  • ChainID: 31337
  • RPC: http://127.0.0.1:8545
  • WSS: ws://127.0.0.1:8545

Với node Forking này, mỗi giao dịch ghi lên blockchain thì sẽ sang 1 block mới.

Lập trình NodeJs kết nối tới Forking node

Đang cập nhật…

Triển khai Explorer giúp xem thông tin dễ dàng hơn

Sau khi Forking chúng ta vẫn cần một giao diện kiểu Explore giống như Etherscan hay Arbiscan để dễ dàng kiểm tra thông tin giao dịch. Sau thời gian tìm kiếm tôi thấy có hai thằng: EthernalBlockscout.

Sử dụng Ethernal

Ethernal là một công cụ mã nguồn mở giúp bạn xem thông tin giao dịch trên các nền tảng dạng EVM blockchain. Toàn bộ mã nguồn bạn có thể xem trên Github: Ethernal Source.

Bạn có thể cài đặt trực tiếp trên máy thông qua hướng dẫn từ tài liệu: Ethernal Documentation. Nhưng cách đơn giản nhất là tạo account trên website Ethernal, đăng nhập và tạo một workspace mới (Ví dụ tôi tạo Arbitrum-Forking theo kiểu Ethereum) và sử dụng RPC: http://127.0.0.1:8545. Lúc này Ethernal sẽ kết nối trực tiếp tới RPC để lấy thông tin và hiển thị trên giao diện. Đây là giao diện sau khi kết nối:

Thông tin giao dịch trên giao diện Ethernal
Thông tin giao dịch trên giao diện Ethernal

Khá tiện và đơn giản để sử dụng không cần phải cài đặt thêm gì, nhưng dữ liệu sẽ không được đồng bộ nếu trình duyệt bị tắt. Nếu muốn đồng bộ ngay cả khi trình duyệt bị tắt thì bạn phải sử dụng thêm plugin hardhat-ethernal.

Thông tin chi tiết các giao dịch hiện thị khá tốt đặt biệt là các event Transfer, tiếc là chỉ hiện address mà không hiện symbol. Nói chung mình thấy Ethernal phù hợp cho nhu cầu hiện tại của mình khi sử dụng node Forking. Nhưng nhiều khi bạn phải refesh lại trang hoặc switch giữa các link mới thấy cập nhật thông tin giao dịch.

Thử một lượt thấy Dev dùng khá tiện:

  • Accounts: Mục này cho phép thêm account vào, có thể dùng private key hoặc address. Account này sẽ được dùng cho các giao dịch ghi lên blockchain.
  • Blocks: Hiển thị các block gần đây.
  • Transaction: Hiển thị các giao dịch gần đây
  • Contract: Có thể import các contract vào đây sau đó cập nhật ABI cho contract. Sau khi cập nhật xong bạn sẽ có giao diện để gọi các hàm của contract và giao dịch để gửi các giao dịch lên blockchain.

Sử dụng Blockscout

Blockscout cũng là một công cụ mã nguồn mở giúp bạn xem thông tin giao dịch trên các nền tảng dạng EVM blockchain. Toàn bộ mã nguồn bạn có thể xem trên Github: Blockscout Source

Với Blockscout bạn có thể triển khai riêng bản trên máy của bạn theo hướng dẫn: Blockscout Manual Deployment. Việc triển khai cũng khá phức tạp đòi hỏi bạn phải cài đặt PostgreSQL.

Do sử dụng được Ethernal rùi nên tôi không cài đặt thằng này. Các bạn nếu muốn có thể cài đặt thử nhé!

Một số vấn đề chú ý với bản Forking

Việc này không đòi hỏi phải đồng bộ dữ liệu về local, do đó bạn không cần phải lo về HDD. Việc Forking này ban đầu để hỗ trợ cho các L1 (Layer 1) dạng EVM, nhưng có thể sử dụng cho L2 (Layer 2), nhưng bạn phải chú ý một số vấn đề. Dưới đây là sự khác nhau khi Forking mạng Arbitrum:

  1. Một số thông tin block giữa mạng Mainnet và mạng Forking thì có sự khác nhau đôi chút. Ví dụ (Có thể còn có nhiều thông tin khác nữa):
    • Mạng Forking không có thông tin L1
    • Giao dịch đầu tiên trong mỗi block của Mainnet và Forking khác nhau
  2. Liên quan tới lới giá trị BlockNumber hiện tại:
    • Trên Mainnet, nếu gọi hàm getBlockNumber() của contract 0xcA11bde05977b3631167028862bE2a173976CA11 nó chỉ trả về BlockNumber của Layer 1. Còn muốn lấy block đúng trên Arbitrum thì phải gọi hàm getBlockNumber() của contract 0x842eC2c7D803033Edf55E478F461FC547Bc54EB2, nó sẽ trả về đúng block number trên Layer 2.
    • Trên mạng lưới Forking thì gọi hàm getBlockNumber() của contract 0x842eC2c7D803033Edf55E478F461FC547Bc54EB2 sẽ bị lỗi luôn, mà phải gọi hàm getBlockNumber() của 0xcA11bde05977b3631167028862bE2a173976CA11
Mạng Mainnet (Trái) và Mạng Forking (Phải)
Mạng Mainnet (Trái) và Mạng Forking (Phải)

Một số lỗi có thể gặp khi cài đặt

Phiên bản NodeJs không đúng

Để cài đặt được yêu cầu bạn phải cài NodeJs từ phiên bản 16 trở lên.

Error: C83B0000:error:1C800066:Provider routines:ossl_gcm_stream_update:cipher operation

Trường hợp cài đặt gặp lỗi “Error: C83B0000:error:1C800066:Provider routines:ossl_gcm_stream_update:cipher operation” (Mình gặp trên Windows) thì đánh lệnh sau trước rùi cài lại:

npm cache clear --force

Lỗi: code ERR_SSL_CIPHER_OPERATION_FAILED

Nếu gặp lỗi “code ERR_SSL_CIPHER_OPERATION_FAILED” thì đánh lệnh sau:

npm install -g https://tls-test.npmjs.com/tls-test-1.0.0.tgz

Error HH801: Plugin @nomicfoundation/hardhat-toolbox requires the following dependencies to be installed

Khi chạy lệnh mà báo lỗi “Error HH801: Plugin @nomicfoundation/hardhat-toolbox requires the following dependencies to be installed” thì thực hiện cài đặt các gói thiếu theo yêu cầu bằng lệnh yarn add hoặc npm install.

Một số lỗi có thể gặp khi kết nối tới Forking Mainnet

Error: VM Exception while processing transaction: reverted with reason string ‘Multicall3: call failed’

Khi tôi tạo “Forking mainnet” cho Arbitrum, và gọi hàm getBlockNumber() của contract 0x842eC2c7D803033Edf55E478F461FC547Bc54EB2 để lấy block number của Layer 2, tức là block number trên Arbitrum thì trả về lỗi:

Error: Returned error: Error: VM Exception while processing transaction: reverted with reason string 'Multicall3: call failed'
    at Object.ErrorResponse (/home/arbitest/dexscanner/collector/node_modules/web3-core-helpers/lib/errors.js:28:19)
    at /home/arbitest/dexscanner/collector/node_modules/web3-core-requestmanager/lib/index.js:300:36
    at /home/arbitest/dexscanner/collector/node_modules/web3-providers-http/lib/index.js:127:13
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  data: {
    message: "Error: VM Exception while processing transaction: reverted with reason string 'Multicall3: call failed'",
    data: '0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000174d756c746963616c6c333a2063616c6c206661694000000000000000000'
  }
}

Sau khi tìm hiểu tôi phát hiện ra là mạng lưới Forking hoạt động giống như mạng lưới L1 vì thế nếu bạn gọi các hàm mới chỉ có trên L2 thì sẽ bị lỗi. Hàm ArbSys(address(100)) chỉ hỗ trợ trên L2 của Arbitrum:

    function getBlockNumber() public view returns (uint256 blockNumber) {
        blockNumber = ArbSys(address(100)).arbBlockNumber();
    }
    function getL1BlockNumber() public view returns (uint256 l1BlockNumber) {
        l1BlockNumber = block.number;
    }

Khi làm việc với mạng Forking thì phải sử dụng hàm getBlockNumber() của contract 0xcA11bde05977b3631167028862bE2a173976CA11 hoặc hàm getL1BlockNumber() của 0x842eC2c7D803033Edf55E478F461FC547Bc54EB2.

Error: Trying to send an incompatible EIP-155 transaction, signed for another chain

Khi tôi gửi một giao dịch thực hiện chuyển ETH sử dụng thư viện Web cũ, giao dịch này có Type=0 thì báo lỗi như sau:

Error: Returned error: Trying to send an incompatible EIP-155 transaction, signed for another chain.
    at Object.ErrorResponse (/home/arbitest/dexscanner/collector/node_modules/web3-core-helpers/lib/errors.js:28:19)
    at /home/arbitest/dexscanner/collector/node_modules/web3-core-requestmanager/lib/index.js:300:36
    at /home/arbitest/dexscanner/collector/node_modules/web3-providers-http/lib/index.js:127:13
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  data: {
    message: 'Trying to send an incompatible EIP-155 transaction, signed for another chain.'
  }
}

Lỗi này do sai chainId. Khi Forking thì mạng Forking sẽ có chainId mới, chainId này có thể do bạn cấu hình, nếu không hệ thống sẽ tự sinh ra chainId mới. Để biết chainId là bao nhiêu bạn gọi lệnh RPC như sau:

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

// Transaction Receipt
curl --data '{"method":"eth_getTransactionReceipt","params":["0xc1b56de56e24fa1f4d361ad293f9b598e1adf712605d5bbe55ba3cffc35f541a"],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST http://localhost:8545

Lệnh sau giúp bạn debug 1 transaction:

// Debug một transaction
curl --data '{"method":"debug_traceTransaction","params":["0xc1b56de56e24fa1f4d361ad293f9b598e1adf712605d5bbe55ba3cffc35f541a"], "id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST http://localhost:8545

Mình thử sử dụng hàm này nhưng thực sự dữ liệu rất nhiều lên đến 90M dữ liệu và khá rối. Lệnh sau sẽ giúp lấy dữ liệu quan trọng giống “Geth Debug Trace_2” trên etherscan (Rất tiếc Hardhat chưa hỗ trợ):

// Hardhat không hỗ trợ tham số tracer
curl --data '{"method":"debug_traceTransaction","params":["0xc1b56de56e24fa1f4d361ad293f9b598e1adf712605d5bbe55ba3cffc35f541a", {"tracer":"callTracer"}], "id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST http://localhost:8545

// Thử tham số khác, nhưng vẫn trả về dữ liệu rất lớn
curl --data '{"method":"debug_traceTransaction","params":["0x0e8f5d1aa145d5dfb80750901614494d7e5598272eef084ae134ef02636f85d0",{"disableStorage":true,"disableStack":true,"enableMemory":false,"enableReturnData":false}], "id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST http://localhost:8545

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