- Thông điệp → Điều người dùng muốn thực hiện (đề xuất đã ký của họ)
- Siêu dữ liệu → Điều thực sự đã xảy ra (kết quả thực thi)
<Buffer 00 bf a0 e8...> thay vì các địa chỉ và chữ ký có thể đọc được.
Hướng dẫn này chỉ cho bạn cách: Giải mã dữ liệu nhị phân đó thành định dạng con người có thể đọc được, trích xuất thông tin hữu ích và hiểu toàn bộ diễn biến giao dịch từ đề xuất đến thực thi.
Luồng trực tiếp, không giải mã
Chạy máy khách tối giản bên dưới. Các cờ bộ lọc loại bỏ giao dịch biểu quyết và giao dịch thất bại, còn mảngaccountInclude giới hạn kết quả ở hoạt động liên quan đến ID chương trình Jupiter.
filters, createdAt cùng với một nhánh transaction ẩn hai phần tử con:
transaction.transaction.transaction→ thông điệp đã kýtransaction.transaction.meta→ siêu dữ liệu thực thi
Uint8Array hiện vẫn chưa thể đọc được.
Khi chạy tập lệnh với hàm giải mã, bạn sẽ thấy cấu trúc lồng nhau thực tế cùng các địa chỉ có thể đọc được:
Giải mã dữ liệu nhị phân
Tại sao cần giải mã? Dữ liệu Laserstream thô chứa chữ ký, khóa tài khoản và hàm băm dưới dạng các đối tượng nhị phânUint8Array không thể đọc được. Bạn cần chuyển đổi chúng thành chuỗi base58 để hiểu giao dịch.
Giải pháp: Laserstream sử dụng Yellowstone gRPC, cung cấp các tiện ích giải mã tích hợp sẵn. Thay vì viết bộ giải mã riêng cho từng loại trường, chúng ta sử dụng một hàm đệ quy để chuyển đổi toàn bộ dữ liệu nhị phân sang định dạng con người có thể đọc được.
Tìm hiểu cấu trúc giao dịch
Bây giờ khi đã có thể xem dữ liệu được giải mã, hãy khám phá hai phần chính trong mỗi bản cập nhật giao dịch Laserstream. Như ví dụ ban đầu, mỗi giao dịch chứa hai đối tượng chính:- Thông điệp (Đề xuất) →
transaction.transaction.transaction→ thông điệp đã ký (đề xuất của người dùng) - Siêu dữ liệu (Thực thi) →
transaction.transaction.meta→ siêu dữ liệu thực thi (phản hồi của trình xác thực)
Đề xuất: mọi nội dung bên trong thông điệp
Người dùng tạo một thông điệp xác định nội dung gì, ai và đến khi nào. Sau đây là cách giải mã từng phần:Tiêu đề giao dịch
numRequiredSignatures cho trình xác thực biết cần xác minh bao nhiêu chữ ký, còn hai giá trị numReadonly* đánh dấu những tài khoản mà môi trường thực thi có thể xử lý ở chế độ chỉ đọc, qua đó cho phép thực thi song song.
Từ điển khóa tài khoản
accountKeys là một danh sách khóa công khai đơn giản đóng vai trò bảng tra cứu. Mọi số nguyên xuất hiện sau đó trong giao dịch — programIdIndex và từng phần tử trong mảng accounts của một lệnh — đều trỏ ngược về danh sách này theo chỉ mục, giúp tiết kiệm hơn một kilobyte cho mỗi thông điệp.
Bảo vệ chống phát lại
recentBlockhash hết hạn khi không còn nằm trong 150 hàm băm khối gần nhất, tương đương khoảng chín mươi giây trên mainnet.
Lệnh: Các lệnh thực tế
- ID chương trình (
programIdIndex): Trỏ đến một địa chỉ trong mảngaccountKeys(ví dụ: chỉ mục 10 =ComputeBudget111111111111111111111111111111) - Tài khoản (
accounts): Một chuỗi được mã hóa bằng base58, biểu thị các chỉ mục tài khoản mà lệnh này tác động đến - Dữ liệu (
data): Dữ liệu lệnh thực tế được mã hóa bằng base58
convertBuffers, các tài khoản xuất hiện dưới dạng base58 nhưng thực tế chứa các chỉ mục tài khoản (ví dụ: "3vtmrQMafzDoG2CBz1iqgXPTnC" được giải mã thành các chỉ mục [21, 19, 12, 17, 2, 6, 1, 22])
Thiết kế này có nghĩa là thay vì lặp lại toàn bộ địa chỉ 32 byte, mỗi lệnh chỉ tham chiếu đến các vị trí trong bảng tra cứu.
Chữ ký: Bằng chứng ủy quyền
signatures chứa các chữ ký mật mã chứng minh rằng những tài khoản bắt buộc đã ủy quyền cho giao dịch này. Số lượng chữ ký phải khớp với header.numRequiredSignatures.
Tra cứu bảng địa chỉ
versioned là true, addressTableLookups sẽ xuất hiện cùng một bảng trên chuỗi và hai danh sách chỉ mục. Bảng tra cứu nâng giới hạn cứng về số lượng địa chỉ lên hàng chục, trong khi vẫn giữ gói tin dưới MTU 1.232 byte.
Giao dịch v1: Ngân sách tính toán trong tiêu đề
Giao dịch v1 (SIMD-0385, Agave 4.2) bổ sung thêm một trường vào thông điệp:transactionConfig.
instructions của giao dịch v1 không bao giờ chứa mục ComputeBudget111111111111111111111111111111. priorityFee là tổng phí tính bằng lamport cho toàn bộ giao dịch, không phải số micro-lamport trên mỗi đơn vị tính toán. Trường null có nghĩa là người gửi chưa thiết lập giá trị này. Các thông điệp cũ và v0 không có transactionConfig, vì vậy sự hiện diện của trường này xác định một giao dịch v1.
Hai điểm cần kiểm tra trong bộ giải mã:
- Trích xuất phí ưu tiên. Đọc
transactionConfig.priorityFeekhi trường này tồn tại và chỉ quay lại quét các lệnh ComputeBudget đối với giao dịch cũ và v0. Mã chỉ quét các lệnh sẽ xác định mọi giao dịch v1 là không trả phí ưu tiên. - Phiên bản Proto.
yellowstone-grpc-proto12.6.0 là bản phát hành đầu tiên chứa các trường v1, cònhelius-laserstream0.8.4 (JavaScript), 0.6.3 (Rust) và 0.2.0 (Go) là các bản phát hành SDK đầu tiên được xây dựng trên phiên bản đó. Các phiên bản cũ âm thầm loại bỏtransactionConfig.
Cách tất cả kết nối với nhau: Luồng xử lý
Sau đây là những gì diễn ra theo nguyên lý cơ bản:- Xây dựng bảng tra cứu:
accountKeysliệt kê tất cả địa chỉ mà giao dịch này sẽ tác động đến - Đặt quy tắc:
headerxác định số lượng chữ ký bắt buộc và những tài khoản nào ở chế độ chỉ đọc - Tạo các lệnh: Mỗi
instructiontrỏ đến:- Một chương trình (thông qua
programIdIndex→accountKeys[index]) - Các tài khoản cần thiết (thông qua
accounts→ nhiều vị tríaccountKeys[index]) - Dữ liệu lệnh (được mã hóa trong
data)
- Một chương trình (thông qua
- Thêm ủy quyền:
signatureschứng minh các tài khoản bắt buộc đã phê duyệt giao dịch này - Đặt thời hạn:
recentBlockhashđảm bảo giao dịch này không thể được phát lại sau đó
Thực thi: mọi nội dung bên trong siêu dữ liệu
Trong khi thông điệp cho biết người dùng muốn làm gì, siêu dữ liệu cho biết điều thực sự đã xảy ra khi các trình xác thực thực thi giao dịch.Thông tin thực thi cơ bản
Thành công/Thất bạierr: null= thành côngerr: {...}= thất bại kèm thông tin chi tiết về lỗifee= số lamport được tính phí cho giao dịch này
accountKeys theo chỉ mục:
- Tài khoản 0: Mất 15000 lamport (thanh toán phí)
- Tài khoản 1: Nhận 1461600 lamport (tài khoản mới được tạo)
- Tài khoản 3: Nhận 2001231920 lamport (tài khoản chương trình)
Chi tiết thực thi nâng cao
Lệnh bên trongCác mẫu giải mã thực tế
Sau đây là các mẫu phổ biến để trích xuất thông tin hữu ích từ những giao dịch đã giải mã:Ví dụ hoàn chỉnh: Bộ giải mã giao dịch hoán đổi Jupiter
Sau đây là ví dụ hoàn chỉnh về cách giải mã các giao dịch hoán đổi Jupiter và trích xuất thông tin hữu ích:Những điểm chính cần ghi nhớ
- Cấu trúc hai phần: Mỗi giao dịch có một thông điệp (nội dung được yêu cầu) và siêu dữ liệu (điều thực sự đã xảy ra)
- Giải mã nhị phân: Sử dụng
bs58.encode()để chuyển đổi các trường nhị phân thành chuỗi base58 có thể đọc được - Tra cứu khóa tài khoản: Các lệnh tham chiếu đến tài khoản theo chỉ mục trong mảng
accountKeys - Theo dõi số dư: So sánh
preBalancesvàpostBalancesđể xem những gì đã thay đổi - Giao dịch v1: Đọc ngân sách tính toán và phí ưu tiên từ
transactionConfigkhi trường này hiện diện; giao dịch v1 không có lệnh ComputeBudget