Solana 문서프로그램 개발하기

FAQ

StackExchange에 질문을 올려주세요.

버클리 패킷 필터(Berkeley Packet Filter, BPF)

Solana 온체인 프로그램은 LLVM 컴파일러 인프라를 통해 실행 및 링크 가능 형식(ELF)으로 컴파일되며, 이는 버클리 패킷 필터(BPF) 바이트코드의 변형을 포함합니다.

Solana는 LLVM 컴파일러 인프라를 사용하기 때문에, LLVM의 BPF 백엔드를 대상으로 할 수 있는 모든 프로그래밍 언어로 프로그램을 작성할 수 있습니다.

BPF는 해석된 가상 머신에서 실행되거나 효율적인 JIT(just-in-time) 컴파일된 네이티브 명령어로 실행될 수 있는 효율적인 명령 세트를 제공합니다.

메모리 맵

Solana SBF 프로그램이 사용하는 가상 주소 메모리 맵은 고정되어 있으며 다음과 같이 구성됩니다

  • 프로그램 코드는 0x100000000에서 시작
  • 스택 데이터는 0x200000000에서 시작
  • 힙 데이터는 0x300000000에서 시작
  • 프로그램 입력 매개변수는 0x400000000에서 시작

위의 가상 주소는 시작 주소이지만 프로그램은 메모리 맵의 일부분에만 접근할 수 있습니다. 프로그램이 접근 권한이 없는 가상 주소를 읽거나 쓰려고 시도하면 패닉이 발생하고, 위반 시도의 주소와 크기를 포함하는 "AccessViolation" 오류가 반환됩니다.

InvalidAccountData

이 프로그램 오류는 여러 이유로 발생할 수 있습니다. 일반적으로 프로그램이 예상하지 않은 계정을 프로그램에 전달할 때 발생하며, 명령어에서 잘못된 위치에 계정이 있거나 실행 중인 명령어와 호환되지 않는 계정일 때 발생합니다.

프로그램 구현에서 크로스-프로그램 명령을 수행할 때 호출하려는 프로그램의 계정을 제공하지 않으면 이 오류가 발생할 수도 있습니다.

InvalidInstructionData

이 프로그램 오류는 명령어를 역직렬화하는 동안 발생할 수 있으며, 전달된 구조가 명령어와 정확히 일치하는지 확인하세요. 필드 사이에 패딩이 있을 수 있습니다. 프로그램이 Rust BorshSerialize 트레이트를 구현한 경우 명령어 타입 T를 패킹하고 언패킹하여 프로그램이 예상하는 정확한 인코딩을 확인해 보세요.

MissingRequiredSignature

일부 명령어는 계정이 서명자여야 합니다. 계정이 서명되어야 하는데 서명되지 않은 경우 이 오류가 반환됩니다.

프로그램 구현에서 서명된 프로그램 주소가 필요한 교차 프로그램 호출을 수행할 때도 이 오류가 발생할 수 있습니다. 이는 invoke_signed에 전달된 서명자 시드가 프로그램 주소를 생성하는 데 사용된 서명자 시드와 일치하지 않을 때 발생합니다 create_program_address.

Stack

SBF는 가변 스택 포인터 대신 스택 프레임을 사용합니다. 각 스택 프레임의 크기는 4KB입니다.

프로그램이 스택 프레임 크기를 위반하면 컴파일러는 초과를 경고로 보고합니다.

예를 들면:

warning: too many stack frames in function, try reducing the number of local variables processed: 1 instructions (0 simplified) remaining: 1 Function solana_sbf_rust_invoked::fn_exceed_stack_frame_size Stack frame size: 4240 bytes

메시지는 스택 프레임을 초과하는 심볼을 식별하지만, 이름이 맹글링(mangled)되어 있을 수 있습니다.

Rust 심볼을 디맹글링하려면 rustfilt를 사용하세요.

위의 경고는 Rust 프로그램에서 나온 것이므로 디맹글링된 심볼 이름은 다음과 같습니다:

warning: too many stack frames in function, try reducing the number of local variables processed: 1 instructions (0 simplified) remaining: 1 Function solana_sbf_rust_invoked::fn_exceed_stack_frame_size Stack frame size: 4240 bytes

경고가 오류 대신 보고되는 이유는 프로그램이 해당 기능을 사용하지 않더라도 일부 종속 크레이트에 스택 프레임 제한을 위반하는 기능이 포함될 수 있기 때문입니다. 프로그램이 런타임에 스택 크기를 위반하면 CallDepth 오류가 보고됩니다.

SBF 스택 프레임은 0x20000000에서 시작하는 가상 주소 범위를 차지합니다.

힙 크기

프로그램은 Rust alloc API를 통해 런타임 힙에 접근할 수 있습니다. 빠른 할당을 위해 간단한 32KB 범프 힙이 사용됩니다. 이 힙은 freerealloc를 지원하지 않습니다.

내부적으로 프로그램은 가상 주소 0x300000000에서 시작하는 32KB 메모리 영역에 접근할 수 있으며, 프로그램의 특정 요구 사항에 따라 맞춤형 힙을 구현할 수 있습니다.

Rust 프로그램은 맞춤형 global_allocator를 정의하여 힙을 직접 구현합니다.

로더

프로그램은 런타임 로더로 배포되고 실행됩니다. 현재 두 가지 로더가 지원됩니다: BPF LoaderBPF loader deprecated입니다.

로더는 서로 다른 애플리케이션 바이너리 인터페이스를 지원할 수 있으므로 개발자는 동일한 로더에 맞게 프로그램을 작성하고 배포해야 합니다. 한 로더용으로 작성된 프로그램을 다른 로더에 배포하면 프로그램의 입력 매개변수 역직렬화가 일치하지 않아 일반적으로 AccessViolation 오류가 발생합니다.

모든 실용적인 목적을 위해 프로그램은 항상 최신 BPF 로더를 대상으로 작성해야 하며, 최신 로더는 명령줄 인터페이스와 자바스크립트 API의 기본값입니다.

배포

SBF 프로그램 배포는 BPF 공유 객체를 프로그램 계정의 데이터에 업로드하고 계정을 실행 가능하게 표시하는 과정입니다. 클라이언트는 BPF 공유 객체를 작은 조각으로 나누어 Write 명령어의 데이터로 로더에 전송하면, 로더는 해당 데이터를 프로그램 계정 데이터에 기록합니다. 모든 조각이 수신되면 클라이언트는 Finalize 명령어를 로더에 전송하고, 로더는 SBF 데이터가 유효한지 확인한 후 프로그램 계정을 실행 가능으로 표시합니다. 프로그램 계정이 실행 가능으로 표시되면 이후 트랜잭션에서 해당 프로그램이 처리할 명령어를 발행할 수 있습니다.

실행 가능한 SBF 프로그램에 명령이 전달되면 로더는 프로그램의 실행 환경을 구성하고, 프로그램의 입력 매개변수를 직렬화하며, 프로그램의 진입점을 호출하고, 발생한 오류를 보고합니다.

자세한 정보는 프로그램 배포하기를 참조하세요.

입력 매개변수 직렬화

SBF 로더는 프로그램 입력 매개변수를 바이트 배열로 직렬화한 후 프로그램의 진입점에 전달하며, 프로그램은 온체인에서 이를 역직렬화할 책임이 있습니다. 더 이상 사용되지 않는 로더와 현재 로더 간의 변경 사항 중 하나는 입력 매개변수가 정렬된 바이트 배열 내에서 다양한 매개변수가 정렬된 오프셋에 위치하도록 직렬화된다는 점입니다. 이를 통해 역직렬화 구현이 바이트 배열을 직접 참조하고 프로그램에 정렬된 포인터를 제공할 수 있습니다.

최신 로더는 프로그램 입력 매개변수를 다음과 같이 직렬화합니다(모든 인코딩은 리틀 엔디안):

  • 8바이트 부호 없는 계정 수
  • 각 계정에 대해
    • 1바이트는 이것이 중복 계정인지 여부를 나타냅니다. 중복이 아니면 값은 0xff이고, 그렇지 않으면 값은 중복된 계정의 인덱스입니다.
    • 중복인 경우: 7바이트 패딩
    • 중복이 아닌 경우:
      • 1바이트 불리언, 계정이 서명자인 경우 true
      • 1바이트 불리언, 계정이 쓰기 가능한 경우 true
      • 1바이트 불리언, 계정이 실행 가능한 경우 true
      • 4바이트 패딩
      • 32바이트의 계정 공개 키
      • 32바이트의 계정 소유자 공개 키
      • 8바이트 부호 없는 계정이 소유한 lamport 수
      • 8바이트 부호 없는 계정 데이터의 바이트 수
      • x바이트의 계정 데이터
      • 10k바이트의 패딩, realloc에 사용됨
      • 오프셋을 8바이트에 정렬하기 위한 충분한 패딩
      • 8바이트 rent epoch
  • 8바이트의 부호 없는 instruction data 수
  • x바이트의 instruction data
  • 32바이트의 프로그램 ID

Is this page helpful?

목차

페이지 편집