Hiểu cấu trúc dữ liệu tạo nên chuỗi khối giúp chúng tôi nghĩ ra những cách sáng tạo để phân tích dữ liệu này.
**Được viết bởi:**NOXX
Biên dịch: Flush
Điều hướng dữ liệu trên chuỗi là một kỹ năng cần thiết cho bất kỳ ai muốn hiểu không gian Web3. Hiểu cấu trúc dữ liệu tạo nên chuỗi khối giúp chúng tôi nghĩ về những cách sáng tạo để phân tích dữ liệu này. Đồng thời, dữ liệu trên chuỗi này tạo thành một phần lớn dữ liệu có sẵn. Bài đăng này sẽ đi sâu vào cấu trúc dữ liệu chính trong EVM, biên nhận giao dịch và nhật ký sự kiện liên quan.
Tại sao đăng nhập
Trước khi bắt đầu, hãy nói ngắn gọn về lý do tại sao chúng ta cần sử dụng nhật ký sự kiện với tư cách là nhà phát triển solidity:
Nhật ký sự kiện là một tùy chọn rẻ hơn để lưu trữ dữ liệu mà hợp đồng không cần truy cập và cũng có thể tái tạo lại trạng thái được lưu trữ bằng cách kiểm tra các biến cụ thể trong hợp đồng thông minh, lập chỉ mục các biến.
Ghi nhật ký sự kiện là một cách để kích hoạt ứng dụng Web3 lắng nghe nhật ký sự kiện cụ thể.
Các nút EVM không cần giữ nhật ký mãi mãi và có thể tiết kiệm dung lượng bằng cách xóa các nhật ký cũ. Hợp đồng không có quyền truy cập vào kho lưu trữ nhật ký, vì vậy các nút không cần chúng để thực hiện hợp đồng. Mặt khác, lưu trữ hợp đồng là cần thiết để thực hiện và do đó không thể xóa được.
Ethereum Block Merkle Root
Trong Phần 4, chúng ta đã đi sâu vào khung Ethereum, đặc biệt là phần gốc Merkle trạng thái. Gốc trạng thái là một trong ba gốc Merkle có trong tiêu đề khối. Hai cái còn lại là Gốc giao dịch và Gốc biên lai.
Đối với đầu vào để xây dựng khuôn khổ này, chúng tôi sẽ đề cập đến khối 15001871 trên Ethereum, khối này chứa 5 giao dịch với các biên nhận và nhật ký sự kiện được liên kết của chúng được gửi.
tiêu đề khối
Chúng ta sẽ bắt đầu với 3 phần trong block header là Transaction Root, Receipt Root và Logs Bloom (có thể xem lại phần giới thiệu sơ lược về block header trong Phần 4).
Nguồn:
Trong ứng dụng khách Ethereum bên dưới Gốc giao dịch và Gốc biên lai, Merkle Patricia Tries chứa tất cả dữ liệu giao dịch và dữ liệu biên nhận trong khối. Bài viết này sẽ chỉ tập trung vào tất cả các giao dịch và biên lai mà một nút có thể truy cập.
Thông tin tiêu đề khối của khối 15001871 được tìm thấy thông qua nút Ethereum như sau:
Các logBloom trong tiêu đề khối là một cấu trúc dữ liệu quan trọng, sẽ được đề cập ở phần sau của bài viết này. Trước tiên, hãy bắt đầu với dữ liệu nằm trong Gốc giao dịch, Trie giao dịch.
Cây giao dịch Cây giao dịch Trie
Giao dịch Trie là một bộ dữ liệu tạo ra các giao dịchRoot và ghi lại các vectơ yêu cầu giao dịch. Các vectơ yêu cầu giao dịch là các mẩu thông tin cần thiết để thực hiện một giao dịch. Các trường dữ liệu chứa trong một giao dịch như sau:
Loại - loại giao dịch (giao dịch truyền thống LegacyTxType, giới thiệu AccessListTxType EIP-2930, giới thiệu DynamicFeeTxType EIP-1559)
ChainId - ID chuỗi EIP155 của giao dịch
Data - dữ liệu đầu vào của giao dịch
AccessList - danh sách truy cập cho các giao dịch
Gas - giới hạn gas của giao dịch
Gasprice - giá gas của giao dịch
GasTipCap - phần thưởng ưu đãi dành cho những người khai thác có gas đơn vị giao dịch vượt quá phí cơ bản để đóng gói trước, maxPriorityFeePerGas trong Geth được xác định bởi EIP1559
GasFeeCap - giới hạn trên của phí gas trên mỗi đơn vị giao dịch, maxFeePerGas trong Geth (GasFeeCap ≥ baseFee + GasTipCap)
Giá trị - số lượng Ethereum được giao dịch
Nonce - nonce của người khởi tạo tài khoản giao dịch
Đến - Địa chỉ người nhận của giao dịch. Đối với giao dịch tạo hợp đồng, To trả về giá trị không
RawSignaturues - Giá trị chữ ký V, R, S của dữ liệu giao dịch
Sau khi hiểu các trường dữ liệu trên, chúng ta hãy xem giao dịch đầu tiên của khối 15001871
Thông qua truy vấn ethclient của Geth, bạn có thể thấy rằng cả ChainId và AccessList đều có "omitempty", nghĩa là nếu trường trống, nó sẽ bị bỏ qua trong phản hồi để giảm hoặc rút ngắn kích thước của dữ liệu được tuần tự hóa.
mã nguồn:
Giao dịch này thể hiện việc chuyển mã thông báo USDT sang địa chỉ 0xec23e787ea25230f74a3da0f515825c1d820f47a. Địa chỉ Đến là địa chỉ hợp đồng ERC20 USDT 0xdac17f958d2ee523a2206206994597c13d831ec7. Thông qua DỮ LIỆU ĐẦU VÀO, chúng ta có thể thấy rằng chữ ký hàm 0xa9059cbb tương ứng với hàm Chuyển (Địa chỉ, UINT256) và 42.251 USDT (độ chính xác là 6) đến 0x2b279b8 (45251000) được chuyển sang 0xEC23E787EA25230F thành 0xEC23E787EA25230F.74A3DA Địa chỉ 0F515825C1D820F47A.
Bạn có thể nhận thấy rằng cấu trúc dữ liệu giao dịch này không cho chúng ta biết bất cứ điều gì về kết quả của giao dịch, vậy giao dịch có thành công không? Nó tiêu thụ bao nhiêu gas? Bản ghi sự kiện nào được kích hoạt? Tại thời điểm này, chúng tôi sẽ giới thiệu Receipt Trie.
Biên nhận Trie
Giống như biên lai mua sắm ghi lại kết quả của giao dịch, một đối tượng trong Receipt Trie thực hiện tương tự đối với giao dịch Ethereum nhưng cũng ghi lại một số chi tiết bổ sung. Quay lại câu hỏi về biên lai giao dịch ở trên, chúng ta sẽ tập trung vào nhật ký đã kích hoạt các sự kiện sau.
Truy vấn lại dữ liệu trên chuỗi của 0x311b và nhận biên lai giao dịch của nó. Tại thời điểm này, sẽ nhận được các trường sau:
mã nguồn:
Loại - loại giao dịch (LegacyTxType, AccessListTxType, DynamicFeeTxType)
PostState(root) - StateRoot, nút gốc của cây trạng thái được tạo ra sau khi thực hiện giao dịch, giá trị tương ứng được tìm thấy trong hình là 0x, có thể là do EIP98
Tích lũy GasUsed - Tổng lượng Gas tích lũy được tiêu thụ bởi giao dịch này và tất cả các giao dịch trước đó trong cùng một khối
Bloom(logsBloom) - Bộ lọc Bloom cho nhật ký sự kiện, được sử dụng để tìm kiếm và truy cập nhật ký sự kiện hợp đồng trên chuỗi khối một cách hiệu quả, cho phép các nút nhanh chóng truy xuất liệu một sự kiện nhất định có xảy ra trong khối hay không mà không cần phân tích cú pháp khối đầy đủ Tất cả biên nhận giao dịch trong khối
Nhật ký - một mảng các đối tượng nhật ký chứa các mục nhật ký được tạo bởi các sự kiện hợp đồng được kích hoạt trong quá trình thực hiện giao dịch
TxHash - hàm băm giao dịch được liên kết với biên nhận
Địa chỉ hợp đồng - Nếu giao dịch là để tạo hợp đồng, địa chỉ nơi hợp đồng được triển khai. Nếu giao dịch không phải là tạo hợp đồng, nhưng chẳng hạn như chuyển giao hoặc tương tác với hợp đồng thông minh đã triển khai, thì trường Địa chỉ hợp đồng sẽ trống
GasUsed - gas tiêu thụ bởi giao dịch này
BlockNumber - số khối của khối nơi giao dịch này xảy ra
TransactionIndex - Chỉ số giao dịch trong khối, chỉ số xác định giao dịch nào được thực hiện trước. Giao dịch này ở trên cùng của khối, vì vậy chỉ số 0
Bây giờ chúng ta đã biết thành phần của biên nhận giao dịch, chúng ta hãy xem xét kỹ hơn về logsBloom và mảng nhật ký ghi lại biên nhận giao dịch.
Nhật ký sự kiện
Thông qua mã hợp đồng USDT trên mạng chính Ethereum, chúng ta có thể thấy rằng sự kiện Chuyển được khai báo ở dòng 86 của hợp đồng và hai tham số đầu vào có từ khóa "được lập chỉ mục".
(mã nguồn:
Khi một đầu vào sự kiện được "lập chỉ mục", nó cho phép chúng tôi nhanh chóng tìm thấy nhật ký thông qua đầu vào đó. Ví dụ: khi sử dụng chỉ mục "từ" ở trên, có thể lấy tất cả nhật ký sự kiện thuộc loại Chuyển giao có địa chỉ "từ" 0x5041ed759dd4afc3a72b8192c143f72f4724081a giữa các khối X và Y. Chúng ta cũng có thể thấy rằng khi hàm truyền được gọi ở dòng 138, nhật ký sự kiện sẽ được kích hoạt. Điều đáng chú ý là hợp đồng hiện tại sử dụng phiên bản trước của solidity, vì vậy từ khóa phát ra bị thiếu.
Quay lại dữ liệu on-chain thu được:
mã nguồn:
Hãy tìm hiểu sâu hơn một chút về các trường địa chỉ, chủ đề và dữ liệu.
Chủ đề chủ đề
Chủ đề là một giá trị chỉ mục. Từ hình trên, chúng ta có thể thấy rằng có 3 tham số chỉ mục của các chủ đề trong dữ liệu truy vấn trên chuỗi, trong khi sự kiện Chuyển chỉ có 2 tham số chỉ mục (từ và đến). Điều này là do chủ đề đầu tiên luôn là hàm băm chữ ký hàm của sự kiện. Chữ ký hàm sự kiện trong ví dụ hiện tại là Chuyển giao(địa chỉ, địa chỉ, uint256). Bằng cách băm nó với keccak256, chúng tôi nhận được kết quả ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef.
(Công cụ trực tuyến:
Khi chúng ta truy vấn trường từ như đã đề cập ở trên, nhưng đồng thời muốn giới hạn loại bản ghi sự kiện truy vấn chỉ thành Bản ghi sự kiện loại chuyển, chúng ta cần lọc theo loại sự kiện bằng cách lập chỉ mục chữ ký sự kiện.
Chúng tôi có thể có tối đa 4 chủ đề, mỗi chủ đề có kích thước 32 byte (nếu loại tham số chỉ mục lớn hơn 32 byte (tức là chuỗi và byte), dữ liệu thực tế không được lưu trữ mà là bản tóm tắt dữ liệu keccak256 được lưu trữ). Chúng ta có thể khai báo 3 tham số chỉ mục vì tham số đầu tiên được lấy bởi chữ ký sự kiện. Nhưng có một tình huống mà chủ đề đầu tiên không phải là chữ ký sự kiện băm. Đây là trường hợp khi khai báo các sự kiện ẩn danh. Điều này mở ra khả năng sử dụng 4 tham số chỉ mục thay vì 3 tham số trước đó, nhưng mất khả năng lập chỉ mục tên sự kiện. Một ưu điểm khác của các sự kiện ẩn danh là chúng ít tốn kém hơn khi triển khai vì chúng không thực thi một chủ đề bổ sung. Các chủ đề khác là giá trị của các chỉ số "từ" và "đến" từ sự kiện Chuyển giao.
Dữ liệuDữ liệu
Phần dữ liệu chứa các thông số còn lại (không được lập chỉ mục) từ nhật ký sự kiện. Trong ví dụ trên, có một giá trị 0x00000000000000000000000000000000000000000000000002b279b8, là 45251000 ở dạng thập phân, là số tiền nói trên là $45,251. Nếu có nhiều tham số như vậy, chúng sẽ được thêm vào mục dữ liệu. Ví dụ dưới đây sẽ chỉ ra trường hợp có nhiều hơn 1 tham số không được lập chỉ mục.
Ví dụ hiện tại thêm trường "thuế" bổ sung vào sự kiện Chuyển. Giả sử thuế đã đặt là 20%, thì giá trị thuế phải là 45251000 * 20% = 9050200, giá trị thập lục phân của nó là 0x8a1858, vì loại số này là uint256 và loại dữ liệu là 32 byte, bạn cần giá trị thập lục phân được lấp đầy bằng 32 byte và kết quả của mục dữ liệu là 0x000000000000000000000000000000000000000000000000000000002b279b800000000000000000000000000000000000 0000 00000000000000000000008a1858.
Địa chỉ
Trường địa chỉ là địa chỉ của hợp đồng đã phát ra sự kiện, một lưu ý quan trọng về trường này là nó sẽ được lập chỉ mục mặc dù nó không được đưa vào phần chủ đề. Lý do là sự kiện Chuyển nhượng là một phần của tiêu chuẩn ERC20, có nghĩa là khi cần lọc nhật ký của các sự kiện chuyển nhượng ERC20, các sự kiện chuyển nhượng sẽ được lấy từ tất cả các hợp đồng ERC20. Và bằng cách lập chỉ mục địa chỉ hợp đồng, tìm kiếm có thể được thu hẹp thành một hợp đồng/mã thông báo cụ thể, chẳng hạn như USDT trong ví dụ.
Opcodes Opcodes
Cuối cùng là LOG opcode. Chúng nằm trong khoảng từ LOG0 khi không có chủ đề nào đến LOG4 khi có 4 chủ đề. LOG3 là những gì chúng tôi sử dụng trong ví dụ của chúng tôi. Chứa những điều sau đây:
offset - bù bộ nhớ, cho biết vị trí bắt đầu của đầu vào trường dữ liệu
độ dài - độ dài của dữ liệu để đọc từ bộ nhớ
chủ đề x(0 - 4) - giá trị của chủ đề x
(Nguồn:
offset và length xác định vị trí của dữ liệu trong phần dữ liệu trong bộ nhớ.
Sau khi hiểu cấu trúc của nhật ký và cách một chủ đề được lập chỉ mục, hãy hiểu cách các mục chỉ mục được tìm kiếm.
Bộ lọc Bloom Bộ lọc Bloom
Bí quyết để lập chỉ mục các mục được tìm kiếm nhanh hơn là bộ lọc Bloom.
Bài báo Llimllib có định nghĩa và giải thích tốt về cấu trúc dữ liệu này.
"Bộ lọc Bloom là một cấu trúc dữ liệu có thể được sử dụng để xác định xem một phần tử có trong bộ sưu tập hay không. Nó có đặc điểm hoạt động nhanh và dung lượng bộ nhớ nhỏ. Chi phí chèn và truy vấn hiệu quả là Bộ lọc Bloom là dữ liệu dựa trên xác suất cấu trúc: Nó chỉ có thể cho chúng ta biết rằng một phần tử chắc chắn không có trong tập hợp hoặc có thể có trong tập hợp. Cấu trúc dữ liệu cơ bản của bộ lọc Bloom là một vectơ bit.”
Dưới đây là một ví dụ về một vectơ bit. Các ô màu trắng biểu thị các bit có giá trị 0 và các ô màu lục biểu thị các bit có giá trị 1.
Các bit này được đặt thành 1 bằng cách lấy một số đầu vào và băm, giá trị băm kết quả được sử dụng làm chỉ mục bit trên bit nào sẽ được cập nhật. Vectơ bit ở trên là kết quả của việc áp dụng 2 giá trị băm khác nhau cho giá trị "ethereum" để có được chỉ mục 2 bit. Băm đại diện cho một số thập lục phân và để lấy chỉ mục, bạn lấy số đó và chuyển đổi nó thành giá trị trong khoảng từ 0 đến 14. Có nhiều cách để làm điều này, như mod 14.
Ôn tập
Với bộ lọc Bloom cho các giao dịch, tức là một vectơ bit, nó có thể được băm trong Ethereum để xác định bit nào trong vectơ bit cần cập nhật. Đầu vào là trường địa chỉ và chủ đề của nhật ký sự kiện. Hãy xem lại nhật kýBloom trong biên nhận giao dịch, đây là bộ lọc Bloom dành riêng cho giao dịch. Một giao dịch có thể có nhiều nhật ký, nhật ký này chứa địa chỉ/chủ đề của tất cả các nhật ký.
Nếu bạn nhìn lại tiêu đề khối, bạn sẽ tìm thấy một logBloom khác. Đây là bộ lọc Bloom cho tất cả các giao dịch trong khối. Trong đó chứa tất cả các địa chỉ/chủ đề trong mỗi nhật ký cho mỗi giao dịch.
Các bộ lọc Bloom này được thể hiện ở dạng thập lục phân chứ không phải nhị phân. Chúng dài 256 byte và đại diện cho một vectơ 2048 bit. Nếu chúng ta tham khảo ví dụ Llimllib ở trên, độ dài vectơ bit của chúng ta là 15 và các chỉ số bit 2 và 13 được lật thành 1. Hãy xem những gì chúng ta nhận được khi chuyển đổi nó sang hex.
Mặc dù biểu diễn thập lục phân không giống như một vectơ bit, nhưng nó có trong logsBloom.
Truy vấn Truy vấn
Một nội dung truy vấn đã được đề cập trước đó, "Tìm tất cả nhật ký sự kiện thuộc loại Chuyển giao có địa chỉ "từ" là 0x5041ed759dd4afc3a72b8192c143f72f4724081a giữa các khối X và Y". Chúng ta có thể lấy chủ đề chữ ký sự kiện, đại diện cho một chủ đề thuộc loại Chuyển giao và giá trị từ (0x5041…) và xác định chỉ số bit nào trong bộ lọc Bloom sẽ được đặt thành 1.
Nếu bạn sử dụng logBloom trong tiêu đề khối, bạn có thể kiểm tra xem liệu có bất kỳ bit nào trong số này không được đặt thành 1 hay không. Nếu không, có thể xác định rằng không có nhật ký nào phù hợp với điều kiện trong khối. Và nếu các bit đó được phát hiện là đã được đặt, chúng tôi biết rằng nhật ký phù hợp có thể nằm trong khối. Nhưng không hoàn toàn chắc chắn, vì tiêu đề khối logBloom bao gồm nhiều địa chỉ và chủ đề. Các bản ghi sự kiện khác có thể có các bit khớp được đặt. Đây là lý do tại sao bộ lọc Bloom là một cấu trúc dữ liệu xác suất. Vectơ bit càng lớn thì khả năng xảy ra xung đột chỉ số bit với các bản ghi khác càng ít. Khi bạn có bộ lọc Bloom phù hợp, bạn có thể sử dụng phương pháp tương tự để truy vấn logBloom cho từng biên lai. Khi có được kết quả phù hợp, mục nhập nhật ký thực tế có thể được xem để truy xuất đối tượng.
Thực hiện các thao tác trên trên các khối X đến Y để nhanh chóng tìm và truy xuất tất cả nhật ký đáp ứng tiêu chí. Đây là cách bộ lọc Bloom hoạt động về mặt khái niệm.
Bây giờ hãy xem triển khai được sử dụng trong Ethereum.
Triển khai Geth - Bộ lọc Bloom
Bây giờ chúng ta đã biết cách hoạt động của bộ lọc Bloom, hãy tìm hiểu cách bộ lọc Bloom hoàn thành việc sàng lọc từng bước từ địa chỉ/chủ đề đến nhật kýBloom trong một khối thực tế.
Trước hết, từ định nghĩa của Sách vàng Ethereum:
Nguồn:
"Chúng tôi xác định chức năng bộ lọc Bloom M để giảm các mục nhật ký thành một hàm băm 256 byte:
TRONG là một bộ lọc Bloom chuyên dụng đặt ba bit vào năm 2048 với một chuỗi byte tùy ý. Điều này được thực hiện bằng cách lấy 11 bit thấp hơn của mỗi trong ba cặp byte đầu tiên trong hàm băm Keccak-256 của chuỗi byte. "
Một ví dụ và tham chiếu đến triển khai ứng dụng khách Geth được cung cấp bên dưới để đơn giản hóa việc hiểu các định nghĩa trên.
Đây là nhật ký giao dịch mà chúng tôi đã xem xét trên Etherscan.
Chủ đề đầu tiên là chữ ký sự kiện 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef và chuyển đổi giá trị này thành chỉ mục bit cần được cập nhật.
Dưới đây là hàm bloomValues từ codebase Geth.
Hàm này nhận chủ đề chữ ký sự kiện, chẳng hạn như: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef và dữ liệu khác, đồng thời trả về chỉ mục bit cần được cập nhật trong bộ lọc Bloom.
mã nguồn:
Hàm bloomValues nhận đầu vào là một chủ đề (chữ ký sự kiện trong ví dụ) và một hashbuf (một mảng byte trống có độ dài 6).
Tham khảo đoạn Giấy vàng, "Ba cặp byte đầu tiên trong hàm băm Keccak-256 của một chuỗi byte". Ba cặp byte này là 6 byte, là độ dài của hashbuf.
Lệnh sha giữa các dòng 140 - 144 băm dữ liệu đầu vào và tải đầu ra vào hashbuf.
Kết quả thập lục phân của đầu ra sha sử dụng keccak256 là (khi sử dụng keccak 256 làm chữ ký hàm, đầu vào là loại văn bản, nhưng ở đây là loại thập lục phân): ada389e1fc24a8587c776340efb91b36e675792ab631816100d55df0b5cf3cbc.
Nội dung hiện tại của hasbuf [ad, a3, 89, e1, fc, 24] (thập lục phân). Mỗi ký tự thập lục phân đại diện cho 4 bit.
3 Tính v1.
hàm băm [1] = 0xa3 = 10100011 cho bit AND với 0x7. 0x7 = 00000111.
Một byte bao gồm 8 bit, nếu bạn muốn lấy chỉ số bit, bạn cần đảm bảo rằng giá trị thu được nằm trong khoảng từ 0 đến 7 của mảng chỉ số 0. Sử dụng bitwise AND để hashbuf [1] Bị giới hạn ở các giá trị từ 0 đến 7. Được tính trong ví dụ, 10100011 & 00000111 = 00000011 = 3.
Giá trị chỉ số bit này được sử dụng với toán tử dịch chuyển bit, tức là dịch chuyển 3 bit sang trái, dẫn đến chỉ số byte 8 bit 00001000, để tạo ra một bit bị lật.
v1 là toàn bộ byte chứ không phải là chỉ mục bit thực tế, bởi vì giá trị này sẽ được ORed bitwise trên bộ lọc Bloom sau này. Hoạt động OR sẽ đảm bảo rằng tất cả các bit tương ứng trong bộ lọc Bloom cũng được lật.
Bây giờ chúng ta có các giá trị byte, nhưng chúng ta vẫn cần chỉ mục byte. Bộ lọc Bloom dài 256 byte (2048 bit), vì vậy chúng ta cần biết byte nào sẽ chạy OR theo bit. Giá trị i1 đại diện cho chỉ mục byte này.
Đặt hashbuf thông qua thứ tự byte uint16 big-endian, làm cho nó giới hạn 2 byte đầu tiên của mảng bit, trong ví dụ này là 0xada3 = 1010110110100011.
Bitwise VÀ giá trị này với 0x7ff = 0000011111111111. Có 11 bit trong đó 0x7ff được đặt thành 1. Như đã đề cập trong bài báo màu vàng, "nó thực hiện điều này bằng cách lấy 11 bit thấp hơn của mỗi trong số ba cặp đầu tiên". Điều này sẽ dẫn đến giá trị 0000010110100011 là 1010110110100011 & 0000011111111111.
Sau đó dịch chuyển giá trị sang phải 3 bit. Điều này chuyển đổi một số có 11 chữ số thành một số có 8 chữ số. Chúng tôi muốn có một chỉ mục byte và độ dài byte của bộ lọc Bloom là 256, vì vậy giá trị chỉ mục byte cần phải nằm trong phạm vi này. Và một số 8 bit có thể là bất kỳ giá trị nào trong khoảng từ 0 đến 255. Trong ví dụ của chúng ta, giá trị này là 0000010110100011 được dịch sang phải 3 bit 10110100 = 180.
Tính toán chỉ số byte của chúng tôi bằng BloomByteLength, biết rằng nó là 256 trừ đi 180 được tính toán, trừ đi 1. Trừ 1 để giữ kết quả trong khoảng từ 0 đến 255. Điều này cung cấp cho chúng tôi chỉ số byte để cập nhật, trong trường hợp này hóa ra là byte 75, đó là cách chúng tôi tính toán i1.
Cập nhật chỉ số bit 3 trong byte thứ 75 của bộ lọc Bloom (0 là chỉ số nên bit thứ 4), có thể được thực hiện bằng cách thực hiện thao tác OR theo bit của v1 trên byte thứ 75 trong bộ lọc Bloom.
Chúng tôi chỉ đề cập đến cặp byte đầu tiên 0xada3, việc này đã được thực hiện lại cho cặp byte 2 và 3. Mỗi địa chỉ/chủ đề sẽ cập nhật 3 bit trong một vectơ 2048 bit. Như đã đề cập trong Sách vàng, "Bộ lọc Bloom đặt ba bit vào năm 2048 với một chuỗi byte tùy ý".
Cập nhật trạng thái cặp byte 2 chỉ số bit 1 trong byte 195 (thực hiện theo quy trình 3 và 4, kết quả được hiển thị trong hình).
Cặp byte 3 cập nhật trạng thái bit chỉ số 4 trong byte 123.
Nếu bit được cập nhật đã bị lật bởi một chủ đề khác, nó sẽ vẫn như cũ. Sẽ chuyển sang 1 nếu không.
Thông qua quy trình hoạt động trên, có thể xác định rằng chủ đề chữ ký sự kiện sẽ lật các bit sau trong bộ lọc Bloom:
Bit chỉ số 3 trong byte 75
bit chỉ số 1 trong byte 195
bit chỉ số 4 trong byte 123
Nhìn vào logBlooms trong biên nhận giao dịch, được chuyển đổi thành nhị phân, xác minh rằng các chỉ số bit này đã được đặt.
Trong khi đó, đối với những độc giả quan tâm đến việc tìm hiểu thêm về việc triển khai tìm kiếm nhật ký và bộ lọc Bloom, bạn có thể tham khảo bài viết BloomBits Trie.
Tại thời điểm này, cuộc thảo luận chuyên sâu của chúng tôi về loạt bài viết về EVM đã kết thúc và chúng tôi sẽ mang đến cho bạn nhiều bài viết kỹ thuật chất lượng cao hơn trong tương lai.
Xem bản gốc
Nội dung chỉ mang tính chất tham khảo, không phải là lời chào mời hay đề nghị. Không cung cấp tư vấn về đầu tư, thuế hoặc pháp lý. Xem Tuyên bố miễn trừ trách nhiệm để biết thêm thông tin về rủi ro.
Đi sâu vào cấu trúc dữ liệu EVM, biên lai giao dịch và nhật ký sự kiện
**Được viết bởi:**NOXX
Biên dịch: Flush
Điều hướng dữ liệu trên chuỗi là một kỹ năng cần thiết cho bất kỳ ai muốn hiểu không gian Web3. Hiểu cấu trúc dữ liệu tạo nên chuỗi khối giúp chúng tôi nghĩ về những cách sáng tạo để phân tích dữ liệu này. Đồng thời, dữ liệu trên chuỗi này tạo thành một phần lớn dữ liệu có sẵn. Bài đăng này sẽ đi sâu vào cấu trúc dữ liệu chính trong EVM, biên nhận giao dịch và nhật ký sự kiện liên quan.
Tại sao đăng nhập
Trước khi bắt đầu, hãy nói ngắn gọn về lý do tại sao chúng ta cần sử dụng nhật ký sự kiện với tư cách là nhà phát triển solidity:
Các nút EVM không cần giữ nhật ký mãi mãi và có thể tiết kiệm dung lượng bằng cách xóa các nhật ký cũ. Hợp đồng không có quyền truy cập vào kho lưu trữ nhật ký, vì vậy các nút không cần chúng để thực hiện hợp đồng. Mặt khác, lưu trữ hợp đồng là cần thiết để thực hiện và do đó không thể xóa được.
Ethereum Block Merkle Root
Trong Phần 4, chúng ta đã đi sâu vào khung Ethereum, đặc biệt là phần gốc Merkle trạng thái. Gốc trạng thái là một trong ba gốc Merkle có trong tiêu đề khối. Hai cái còn lại là Gốc giao dịch và Gốc biên lai.
Đối với đầu vào để xây dựng khuôn khổ này, chúng tôi sẽ đề cập đến khối 15001871 trên Ethereum, khối này chứa 5 giao dịch với các biên nhận và nhật ký sự kiện được liên kết của chúng được gửi.
tiêu đề khối
Chúng ta sẽ bắt đầu với 3 phần trong block header là Transaction Root, Receipt Root và Logs Bloom (có thể xem lại phần giới thiệu sơ lược về block header trong Phần 4).
Nguồn:
Trong ứng dụng khách Ethereum bên dưới Gốc giao dịch và Gốc biên lai, Merkle Patricia Tries chứa tất cả dữ liệu giao dịch và dữ liệu biên nhận trong khối. Bài viết này sẽ chỉ tập trung vào tất cả các giao dịch và biên lai mà một nút có thể truy cập.
Thông tin tiêu đề khối của khối 15001871 được tìm thấy thông qua nút Ethereum như sau:
Các logBloom trong tiêu đề khối là một cấu trúc dữ liệu quan trọng, sẽ được đề cập ở phần sau của bài viết này. Trước tiên, hãy bắt đầu với dữ liệu nằm trong Gốc giao dịch, Trie giao dịch.
Cây giao dịch Cây giao dịch Trie
Giao dịch Trie là một bộ dữ liệu tạo ra các giao dịchRoot và ghi lại các vectơ yêu cầu giao dịch. Các vectơ yêu cầu giao dịch là các mẩu thông tin cần thiết để thực hiện một giao dịch. Các trường dữ liệu chứa trong một giao dịch như sau:
Sau khi hiểu các trường dữ liệu trên, chúng ta hãy xem giao dịch đầu tiên của khối 15001871
Thông qua truy vấn ethclient của Geth, bạn có thể thấy rằng cả ChainId và AccessList đều có "omitempty", nghĩa là nếu trường trống, nó sẽ bị bỏ qua trong phản hồi để giảm hoặc rút ngắn kích thước của dữ liệu được tuần tự hóa.
mã nguồn:
Giao dịch này thể hiện việc chuyển mã thông báo USDT sang địa chỉ 0xec23e787ea25230f74a3da0f515825c1d820f47a. Địa chỉ Đến là địa chỉ hợp đồng ERC20 USDT 0xdac17f958d2ee523a2206206994597c13d831ec7. Thông qua DỮ LIỆU ĐẦU VÀO, chúng ta có thể thấy rằng chữ ký hàm 0xa9059cbb tương ứng với hàm Chuyển (Địa chỉ, UINT256) và 42.251 USDT (độ chính xác là 6) đến 0x2b279b8 (45251000) được chuyển sang 0xEC23E787EA25230F thành 0xEC23E787EA25230F.74A3DA Địa chỉ 0F515825C1D820F47A.
Bạn có thể nhận thấy rằng cấu trúc dữ liệu giao dịch này không cho chúng ta biết bất cứ điều gì về kết quả của giao dịch, vậy giao dịch có thành công không? Nó tiêu thụ bao nhiêu gas? Bản ghi sự kiện nào được kích hoạt? Tại thời điểm này, chúng tôi sẽ giới thiệu Receipt Trie.
Biên nhận Trie
Giống như biên lai mua sắm ghi lại kết quả của giao dịch, một đối tượng trong Receipt Trie thực hiện tương tự đối với giao dịch Ethereum nhưng cũng ghi lại một số chi tiết bổ sung. Quay lại câu hỏi về biên lai giao dịch ở trên, chúng ta sẽ tập trung vào nhật ký đã kích hoạt các sự kiện sau.
Truy vấn lại dữ liệu trên chuỗi của 0x311b và nhận biên lai giao dịch của nó. Tại thời điểm này, sẽ nhận được các trường sau:
mã nguồn:
Bây giờ chúng ta đã biết thành phần của biên nhận giao dịch, chúng ta hãy xem xét kỹ hơn về logsBloom và mảng nhật ký ghi lại biên nhận giao dịch.
Nhật ký sự kiện
Thông qua mã hợp đồng USDT trên mạng chính Ethereum, chúng ta có thể thấy rằng sự kiện Chuyển được khai báo ở dòng 86 của hợp đồng và hai tham số đầu vào có từ khóa "được lập chỉ mục".
(mã nguồn:
Khi một đầu vào sự kiện được "lập chỉ mục", nó cho phép chúng tôi nhanh chóng tìm thấy nhật ký thông qua đầu vào đó. Ví dụ: khi sử dụng chỉ mục "từ" ở trên, có thể lấy tất cả nhật ký sự kiện thuộc loại Chuyển giao có địa chỉ "từ" 0x5041ed759dd4afc3a72b8192c143f72f4724081a giữa các khối X và Y. Chúng ta cũng có thể thấy rằng khi hàm truyền được gọi ở dòng 138, nhật ký sự kiện sẽ được kích hoạt. Điều đáng chú ý là hợp đồng hiện tại sử dụng phiên bản trước của solidity, vì vậy từ khóa phát ra bị thiếu.
Quay lại dữ liệu on-chain thu được:
mã nguồn:
Hãy tìm hiểu sâu hơn một chút về các trường địa chỉ, chủ đề và dữ liệu.
Chủ đề chủ đề
Chủ đề là một giá trị chỉ mục. Từ hình trên, chúng ta có thể thấy rằng có 3 tham số chỉ mục của các chủ đề trong dữ liệu truy vấn trên chuỗi, trong khi sự kiện Chuyển chỉ có 2 tham số chỉ mục (từ và đến). Điều này là do chủ đề đầu tiên luôn là hàm băm chữ ký hàm của sự kiện. Chữ ký hàm sự kiện trong ví dụ hiện tại là Chuyển giao(địa chỉ, địa chỉ, uint256). Bằng cách băm nó với keccak256, chúng tôi nhận được kết quả ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef.
(Công cụ trực tuyến:
Khi chúng ta truy vấn trường từ như đã đề cập ở trên, nhưng đồng thời muốn giới hạn loại bản ghi sự kiện truy vấn chỉ thành Bản ghi sự kiện loại chuyển, chúng ta cần lọc theo loại sự kiện bằng cách lập chỉ mục chữ ký sự kiện.
Chúng tôi có thể có tối đa 4 chủ đề, mỗi chủ đề có kích thước 32 byte (nếu loại tham số chỉ mục lớn hơn 32 byte (tức là chuỗi và byte), dữ liệu thực tế không được lưu trữ mà là bản tóm tắt dữ liệu keccak256 được lưu trữ). Chúng ta có thể khai báo 3 tham số chỉ mục vì tham số đầu tiên được lấy bởi chữ ký sự kiện. Nhưng có một tình huống mà chủ đề đầu tiên không phải là chữ ký sự kiện băm. Đây là trường hợp khi khai báo các sự kiện ẩn danh. Điều này mở ra khả năng sử dụng 4 tham số chỉ mục thay vì 3 tham số trước đó, nhưng mất khả năng lập chỉ mục tên sự kiện. Một ưu điểm khác của các sự kiện ẩn danh là chúng ít tốn kém hơn khi triển khai vì chúng không thực thi một chủ đề bổ sung. Các chủ đề khác là giá trị của các chỉ số "từ" và "đến" từ sự kiện Chuyển giao.
Dữ liệuDữ liệu
Phần dữ liệu chứa các thông số còn lại (không được lập chỉ mục) từ nhật ký sự kiện. Trong ví dụ trên, có một giá trị 0x00000000000000000000000000000000000000000000000002b279b8, là 45251000 ở dạng thập phân, là số tiền nói trên là $45,251. Nếu có nhiều tham số như vậy, chúng sẽ được thêm vào mục dữ liệu. Ví dụ dưới đây sẽ chỉ ra trường hợp có nhiều hơn 1 tham số không được lập chỉ mục.
Ví dụ hiện tại thêm trường "thuế" bổ sung vào sự kiện Chuyển. Giả sử thuế đã đặt là 20%, thì giá trị thuế phải là 45251000 * 20% = 9050200, giá trị thập lục phân của nó là 0x8a1858, vì loại số này là uint256 và loại dữ liệu là 32 byte, bạn cần giá trị thập lục phân được lấp đầy bằng 32 byte và kết quả của mục dữ liệu là 0x000000000000000000000000000000000000000000000000000000002b279b800000000000000000000000000000000000 0000 00000000000000000000008a1858.
Địa chỉ
Trường địa chỉ là địa chỉ của hợp đồng đã phát ra sự kiện, một lưu ý quan trọng về trường này là nó sẽ được lập chỉ mục mặc dù nó không được đưa vào phần chủ đề. Lý do là sự kiện Chuyển nhượng là một phần của tiêu chuẩn ERC20, có nghĩa là khi cần lọc nhật ký của các sự kiện chuyển nhượng ERC20, các sự kiện chuyển nhượng sẽ được lấy từ tất cả các hợp đồng ERC20. Và bằng cách lập chỉ mục địa chỉ hợp đồng, tìm kiếm có thể được thu hẹp thành một hợp đồng/mã thông báo cụ thể, chẳng hạn như USDT trong ví dụ.
Opcodes Opcodes
Cuối cùng là LOG opcode. Chúng nằm trong khoảng từ LOG0 khi không có chủ đề nào đến LOG4 khi có 4 chủ đề. LOG3 là những gì chúng tôi sử dụng trong ví dụ của chúng tôi. Chứa những điều sau đây:
(Nguồn:
offset và length xác định vị trí của dữ liệu trong phần dữ liệu trong bộ nhớ.
Sau khi hiểu cấu trúc của nhật ký và cách một chủ đề được lập chỉ mục, hãy hiểu cách các mục chỉ mục được tìm kiếm.
Bộ lọc Bloom Bộ lọc Bloom
Bí quyết để lập chỉ mục các mục được tìm kiếm nhanh hơn là bộ lọc Bloom.
Bài báo Llimllib có định nghĩa và giải thích tốt về cấu trúc dữ liệu này.
"Bộ lọc Bloom là một cấu trúc dữ liệu có thể được sử dụng để xác định xem một phần tử có trong bộ sưu tập hay không. Nó có đặc điểm hoạt động nhanh và dung lượng bộ nhớ nhỏ. Chi phí chèn và truy vấn hiệu quả là Bộ lọc Bloom là dữ liệu dựa trên xác suất cấu trúc: Nó chỉ có thể cho chúng ta biết rằng một phần tử chắc chắn không có trong tập hợp hoặc có thể có trong tập hợp. Cấu trúc dữ liệu cơ bản của bộ lọc Bloom là một vectơ bit.”
Dưới đây là một ví dụ về một vectơ bit. Các ô màu trắng biểu thị các bit có giá trị 0 và các ô màu lục biểu thị các bit có giá trị 1.
Các bit này được đặt thành 1 bằng cách lấy một số đầu vào và băm, giá trị băm kết quả được sử dụng làm chỉ mục bit trên bit nào sẽ được cập nhật. Vectơ bit ở trên là kết quả của việc áp dụng 2 giá trị băm khác nhau cho giá trị "ethereum" để có được chỉ mục 2 bit. Băm đại diện cho một số thập lục phân và để lấy chỉ mục, bạn lấy số đó và chuyển đổi nó thành giá trị trong khoảng từ 0 đến 14. Có nhiều cách để làm điều này, như mod 14.
Ôn tập
Với bộ lọc Bloom cho các giao dịch, tức là một vectơ bit, nó có thể được băm trong Ethereum để xác định bit nào trong vectơ bit cần cập nhật. Đầu vào là trường địa chỉ và chủ đề của nhật ký sự kiện. Hãy xem lại nhật kýBloom trong biên nhận giao dịch, đây là bộ lọc Bloom dành riêng cho giao dịch. Một giao dịch có thể có nhiều nhật ký, nhật ký này chứa địa chỉ/chủ đề của tất cả các nhật ký.
Nếu bạn nhìn lại tiêu đề khối, bạn sẽ tìm thấy một logBloom khác. Đây là bộ lọc Bloom cho tất cả các giao dịch trong khối. Trong đó chứa tất cả các địa chỉ/chủ đề trong mỗi nhật ký cho mỗi giao dịch.
Các bộ lọc Bloom này được thể hiện ở dạng thập lục phân chứ không phải nhị phân. Chúng dài 256 byte và đại diện cho một vectơ 2048 bit. Nếu chúng ta tham khảo ví dụ Llimllib ở trên, độ dài vectơ bit của chúng ta là 15 và các chỉ số bit 2 và 13 được lật thành 1. Hãy xem những gì chúng ta nhận được khi chuyển đổi nó sang hex.
Mặc dù biểu diễn thập lục phân không giống như một vectơ bit, nhưng nó có trong logsBloom.
Truy vấn Truy vấn
Một nội dung truy vấn đã được đề cập trước đó, "Tìm tất cả nhật ký sự kiện thuộc loại Chuyển giao có địa chỉ "từ" là 0x5041ed759dd4afc3a72b8192c143f72f4724081a giữa các khối X và Y". Chúng ta có thể lấy chủ đề chữ ký sự kiện, đại diện cho một chủ đề thuộc loại Chuyển giao và giá trị từ (0x5041…) và xác định chỉ số bit nào trong bộ lọc Bloom sẽ được đặt thành 1.
Nếu bạn sử dụng logBloom trong tiêu đề khối, bạn có thể kiểm tra xem liệu có bất kỳ bit nào trong số này không được đặt thành 1 hay không. Nếu không, có thể xác định rằng không có nhật ký nào phù hợp với điều kiện trong khối. Và nếu các bit đó được phát hiện là đã được đặt, chúng tôi biết rằng nhật ký phù hợp có thể nằm trong khối. Nhưng không hoàn toàn chắc chắn, vì tiêu đề khối logBloom bao gồm nhiều địa chỉ và chủ đề. Các bản ghi sự kiện khác có thể có các bit khớp được đặt. Đây là lý do tại sao bộ lọc Bloom là một cấu trúc dữ liệu xác suất. Vectơ bit càng lớn thì khả năng xảy ra xung đột chỉ số bit với các bản ghi khác càng ít. Khi bạn có bộ lọc Bloom phù hợp, bạn có thể sử dụng phương pháp tương tự để truy vấn logBloom cho từng biên lai. Khi có được kết quả phù hợp, mục nhập nhật ký thực tế có thể được xem để truy xuất đối tượng.
Thực hiện các thao tác trên trên các khối X đến Y để nhanh chóng tìm và truy xuất tất cả nhật ký đáp ứng tiêu chí. Đây là cách bộ lọc Bloom hoạt động về mặt khái niệm.
Bây giờ hãy xem triển khai được sử dụng trong Ethereum.
Triển khai Geth - Bộ lọc Bloom
Bây giờ chúng ta đã biết cách hoạt động của bộ lọc Bloom, hãy tìm hiểu cách bộ lọc Bloom hoàn thành việc sàng lọc từng bước từ địa chỉ/chủ đề đến nhật kýBloom trong một khối thực tế.
Trước hết, từ định nghĩa của Sách vàng Ethereum:
Nguồn:
"Chúng tôi xác định chức năng bộ lọc Bloom M để giảm các mục nhật ký thành một hàm băm 256 byte:
TRONG
là một bộ lọc Bloom chuyên dụng đặt ba bit vào năm 2048 với một chuỗi byte tùy ý. Điều này được thực hiện bằng cách lấy 11 bit thấp hơn của mỗi trong ba cặp byte đầu tiên trong hàm băm Keccak-256 của chuỗi byte. "
Một ví dụ và tham chiếu đến triển khai ứng dụng khách Geth được cung cấp bên dưới để đơn giản hóa việc hiểu các định nghĩa trên.
Đây là nhật ký giao dịch mà chúng tôi đã xem xét trên Etherscan.
Chủ đề đầu tiên là chữ ký sự kiện 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef và chuyển đổi giá trị này thành chỉ mục bit cần được cập nhật.
Dưới đây là hàm bloomValues từ codebase Geth.
Hàm này nhận chủ đề chữ ký sự kiện, chẳng hạn như: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef và dữ liệu khác, đồng thời trả về chỉ mục bit cần được cập nhật trong bộ lọc Bloom.
mã nguồn:
Tham khảo đoạn Giấy vàng, "Ba cặp byte đầu tiên trong hàm băm Keccak-256 của một chuỗi byte". Ba cặp byte này là 6 byte, là độ dài của hashbuf.
Dữ liệu mẫu: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef.
3 Tính v1.
hàm băm [1] = 0xa3 = 10100011 cho bit AND với 0x7. 0x7 = 00000111.
Một byte bao gồm 8 bit, nếu bạn muốn lấy chỉ số bit, bạn cần đảm bảo rằng giá trị thu được nằm trong khoảng từ 0 đến 7 của mảng chỉ số 0. Sử dụng bitwise AND để hashbuf [1] Bị giới hạn ở các giá trị từ 0 đến 7. Được tính trong ví dụ, 10100011 & 00000111 = 00000011 = 3.
Giá trị chỉ số bit này được sử dụng với toán tử dịch chuyển bit, tức là dịch chuyển 3 bit sang trái, dẫn đến chỉ số byte 8 bit 00001000, để tạo ra một bit bị lật.
v1 là toàn bộ byte chứ không phải là chỉ mục bit thực tế, bởi vì giá trị này sẽ được ORed bitwise trên bộ lọc Bloom sau này. Hoạt động OR sẽ đảm bảo rằng tất cả các bit tương ứng trong bộ lọc Bloom cũng được lật.
Đặt hashbuf thông qua thứ tự byte uint16 big-endian, làm cho nó giới hạn 2 byte đầu tiên của mảng bit, trong ví dụ này là 0xada3 = 1010110110100011.
Bitwise VÀ giá trị này với 0x7ff = 0000011111111111. Có 11 bit trong đó 0x7ff được đặt thành 1. Như đã đề cập trong bài báo màu vàng, "nó thực hiện điều này bằng cách lấy 11 bit thấp hơn của mỗi trong số ba cặp đầu tiên". Điều này sẽ dẫn đến giá trị 0000010110100011 là 1010110110100011 & 0000011111111111.
Sau đó dịch chuyển giá trị sang phải 3 bit. Điều này chuyển đổi một số có 11 chữ số thành một số có 8 chữ số. Chúng tôi muốn có một chỉ mục byte và độ dài byte của bộ lọc Bloom là 256, vì vậy giá trị chỉ mục byte cần phải nằm trong phạm vi này. Và một số 8 bit có thể là bất kỳ giá trị nào trong khoảng từ 0 đến 255. Trong ví dụ của chúng ta, giá trị này là 0000010110100011 được dịch sang phải 3 bit 10110100 = 180.
Tính toán chỉ số byte của chúng tôi bằng BloomByteLength, biết rằng nó là 256 trừ đi 180 được tính toán, trừ đi 1. Trừ 1 để giữ kết quả trong khoảng từ 0 đến 255. Điều này cung cấp cho chúng tôi chỉ số byte để cập nhật, trong trường hợp này hóa ra là byte 75, đó là cách chúng tôi tính toán i1.
Chúng tôi chỉ đề cập đến cặp byte đầu tiên 0xada3, việc này đã được thực hiện lại cho cặp byte 2 và 3. Mỗi địa chỉ/chủ đề sẽ cập nhật 3 bit trong một vectơ 2048 bit. Như đã đề cập trong Sách vàng, "Bộ lọc Bloom đặt ba bit vào năm 2048 với một chuỗi byte tùy ý".
Cập nhật trạng thái cặp byte 2 chỉ số bit 1 trong byte 195 (thực hiện theo quy trình 3 và 4, kết quả được hiển thị trong hình).
Cặp byte 3 cập nhật trạng thái bit chỉ số 4 trong byte 123.
Nếu bit được cập nhật đã bị lật bởi một chủ đề khác, nó sẽ vẫn như cũ. Sẽ chuyển sang 1 nếu không.
Thông qua quy trình hoạt động trên, có thể xác định rằng chủ đề chữ ký sự kiện sẽ lật các bit sau trong bộ lọc Bloom:
Nhìn vào logBlooms trong biên nhận giao dịch, được chuyển đổi thành nhị phân, xác minh rằng các chỉ số bit này đã được đặt.
Trong khi đó, đối với những độc giả quan tâm đến việc tìm hiểu thêm về việc triển khai tìm kiếm nhật ký và bộ lọc Bloom, bạn có thể tham khảo bài viết BloomBits Trie.
Tại thời điểm này, cuộc thảo luận chuyên sâu của chúng tôi về loạt bài viết về EVM đã kết thúc và chúng tôi sẽ mang đến cho bạn nhiều bài viết kỹ thuật chất lượng cao hơn trong tương lai.