Tài liệu SolanaPhát triển chương trình

FAQ

Đăng câu hỏi của bạn trên StackExchange.

Berkeley Packet Filter (BPF)

Các chương trình onchain của Solana được biên dịch thông qua cơ sở hạ tầng trình biên dịch LLVM thành Định dạng Thực thi và Liên kết (ELF) chứa một biến thể của Berkeley Packet Filter (BPF) bytecode.

Vì Solana sử dụng cơ sở hạ tầng trình biên dịch LLVM, một chương trình có thể được viết bằng bất kỳ ngôn ngữ lập trình nào có thể nhắm đến backend BPF của LLVM.

BPF cung cấp một tập lệnh hiệu quả có thể được thực thi trong một máy ảo thông dịch hoặc như các lệnh gốc được biên dịch just-in-time hiệu quả.

Sơ đồ bộ nhớ

Sơ đồ bộ nhớ địa chỉ ảo được sử dụng bởi các chương trình SBF của Solana được cố định và bố trí như sau

  • Mã chương trình bắt đầu tại 0x100000000
  • Dữ liệu ngăn xếp bắt đầu tại 0x200000000
  • Dữ liệu heap bắt đầu tại 0x300000000
  • Tham số đầu vào chương trình bắt đầu tại 0x400000000

Các địa chỉ ảo trên là địa chỉ bắt đầu nhưng các chương trình được cấp quyền truy cập vào một tập con của sơ đồ bộ nhớ. Chương trình sẽ báo lỗi nếu nó cố gắng đọc hoặc ghi vào một địa chỉ ảo mà nó không được cấp quyền truy cập, và một lỗi AccessViolation sẽ được trả về chứa địa chỉ và kích thước của vi phạm đã cố gắng thực hiện.

InvalidAccountData

Lỗi chương trình này có thể xảy ra vì nhiều lý do. Thông thường, nó được gây ra bởi việc truyền một tài khoản cho chương trình mà chương trình không mong đợi, hoặc ở vị trí sai trong lệnh hoặc một tài khoản không tương thích với lệnh đang được thực thi.

Một triển khai của chương trình cũng có thể gây ra lỗi này khi thực hiện một lệnh chéo giữa các chương trình và quên cung cấp tài khoản cho chương trình mà bạn đang gọi.

InvalidInstructionData

Lỗi chương trình này có thể xảy ra khi đang cố gắng giải tuần tự hóa (deserialize) chỉ thị, hãy kiểm tra rằng cấu trúc được truyền vào khớp chính xác với chỉ thị. Có thể có một số đệm giữa các trường. Nếu chương trình triển khai trait Rust BorshSerialize thì hãy thử đóng gói và giải nén loại chỉ thị Instruction để xác định mã hóa chính xác mà chương trình mong đợi.

MissingRequiredSignature

Một số chỉ thị yêu cầu tài khoản phải là người ký; lỗi này được trả về nếu một tài khoản được mong đợi sẽ được ký nhưng không được ký.

Việc triển khai một chương trình cũng có thể gây ra lỗi này khi thực hiện lệnh gọi chéo chương trình yêu cầu một địa chỉ chương trình đã ký, nhưng các seed người ký được truyền vào invoke_signed không khớp với các seed người ký được sử dụng để tạo địa chỉ chương trình create_program_address.

Stack

SBF sử dụng các khung ngăn xếp thay vì con trỏ ngăn xếp biến. Mỗi khung ngăn xếp có kích thước 4KB.

Nếu một chương trình vi phạm kích thước khung ngăn xếp đó, trình biên dịch sẽ báo cáo việc vượt quá dưới dạng cảnh báo.

Ví dụ:

warning: too many stack frames in function,
the stack frame size is 0x400 but function is using 0x410

Thông báo xác định biểu tượng nào đang vượt quá khung ngăn xếp của nó, nhưng tên có thể bị làm rối.

Để giải mã một biểu tượng Rust, hãy sử dụng rustfilt.

Cảnh báo trên đến từ một chương trình Rust, vì vậy tên biểu tượng đã được giải mã là:

solana_escrow::processor::process_init_escrow::h3f6d86c08b4d54e9

Lý do tại sao một cảnh báo được báo cáo thay vì một lỗi là vì một số crate phụ thuộc có thể bao gồm chức năng vi phạm các hạn chế khung ngăn xếp ngay cả khi chương trình không sử dụng chức năng đó. Nếu chương trình vi phạm kích thước ngăn xếp khi chạy, một lỗi CallDepth sẽ được báo cáo.

Các khung ngăn xếp SBF chiếm một phạm vi địa chỉ ảo bắt đầu từ 0x200000000.

Kích thước heap

Các chương trình có thể truy cập vào heap thời gian chạy thông qua các API alloc của Rust. Để tạo điều kiện cho việc cấp phát nhanh, một heap bump đơn giản 32KB được sử dụng. Heap không hỗ trợ free hoặc realloc.

Về mặt nội bộ, các chương trình có quyền truy cập vào vùng bộ nhớ 32KB bắt đầu từ địa chỉ ảo 0x300000000 và có thể triển khai heap tùy chỉnh dựa trên nhu cầu cụ thể của chương trình.

Các chương trình Rust triển khai heap trực tiếp bằng cách định nghĩa một global_allocator tùy chỉnh

Loaders

Các chương trình được triển khai và thực thi bởi các loaders thời gian chạy, hiện tại có hai loaders được hỗ trợ BPF LoaderBPF loader deprecated

Các loaders có thể hỗ trợ các giao diện nhị phân ứng dụng khác nhau, vì vậy các nhà phát triển phải viết chương trình của họ cho và triển khai chúng vào cùng một loader. Nếu một chương trình được viết cho một loader nhưng lại được triển khai vào một loader khác, kết quả thường là lỗi AccessViolation do không khớp khi giải mã các tham số đầu vào của chương trình.

Về mặt thực tế, chương trình nên luôn được viết để nhắm đến BPF loader mới nhất và loader mới nhất là mặc định cho giao diện dòng lệnh và các API javascript.

Triển khai

Triển khai chương trình SBF là quá trình tải một đối tượng chia sẻ BPF vào dữ liệu tài khoản chương trình và đánh dấu tài khoản có thể thực thi. Một client chia đối tượng chia sẻ SBF thành các phần nhỏ hơn và gửi chúng như dữ liệu hướng dẫn của các hướng dẫn Write đến loader, nơi loader ghi dữ liệu đó vào dữ liệu tài khoản của chương trình. Khi tất cả các phần đã được nhận, client gửi một hướng dẫn Finalize đến loader, sau đó loader xác nhận rằng dữ liệu SBF hợp lệ và đánh dấu tài khoản chương trình là executable. Khi tài khoản chương trình đã được đánh dấu là có thể thực thi, các giao dịch tiếp theo có thể đưa ra hướng dẫn cho chương trình đó xử lý.

Khi một instruction được gửi đến một chương trình SBF thực thi, bộ tải cấu hình môi trường thực thi của chương trình, tuần tự hóa các tham số đầu vào của chương trình, gọi điểm vào của chương trình và báo cáo mọi lỗi gặp phải.

Để biết thêm thông tin, xem triển khai chương trình.

Tuần tự hóa tham số đầu vào

Bộ tải SBF tuần tự hóa các tham số đầu vào của chương trình thành một mảng byte sau đó được truyền đến điểm vào của chương trình, nơi chương trình chịu trách nhiệm giải tuần tự hóa trên chuỗi. Một trong những thay đổi giữa bộ tải đã lỗi thời và bộ tải hiện tại là các tham số đầu vào được tuần tự hóa theo cách khiến các tham số khác nhau rơi vào các vị trí căn chỉnh trong mảng byte đã căn chỉnh. Điều này cho phép các triển khai giải tuần tự hóa tham chiếu trực tiếp đến mảng byte và cung cấp các con trỏ đã căn chỉnh cho chương trình.

Bộ tải mới nhất tuần tự hóa các tham số đầu vào của chương trình như sau (tất cả mã hóa là little endian):

  • 8 byte số không dấu của tài khoản
  • Cho mỗi tài khoản
    • 1 byte chỉ ra nếu đây là tài khoản trùng lặp, nếu không trùng lặp thì giá trị là 0xff, nếu không thì giá trị là chỉ số của tài khoản mà nó trùng lặp với.
    • Nếu trùng lặp: 7 byte đệm
    • Nếu không trùng lặp:
      • 1 byte boolean, true nếu tài khoản là người ký
      • 1 byte boolean, true nếu tài khoản có thể ghi
      • 1 byte boolean, true nếu tài khoản có thể thực thi
      • 4 byte đệm
      • 32 byte của khóa công khai tài khoản
      • 32 byte của khóa công khai chủ sở hữu tài khoản
      • 8 byte số không dấu của lamport thuộc sở hữu của tài khoản
      • 8 byte số không dấu của byte dữ liệu tài khoản
      • x byte dữ liệu tài khoản
      • 10k byte đệm, được sử dụng cho realloc
      • đủ đệm để căn chỉnh offset thành 8 byte.
      • 8 byte kỷ nguyên rent
  • 8 byte của số không dấu của instruction data
  • x byte của instruction data
  • 32 byte của program id

Is this page helpful?

Mục lục

Chỉnh sửa trang