Solana · Chapter 1

Solana Account 구조와 계정 유형 이해하기

작성자: Choi.eth

16 min read

솔라나의 공식 문서를 바탕으로 지금까지 공부한 Address, Account, Program, 계정 유형과 소유권(Owner)의 관계를 정리했습니다. 이더리움과 구조적으로 다른 부분이 많기 때문에, 각 개념이 어떤 관계를 갖는지 중심으로 살펴보겠습니다.

1. Solana의 Address와 Account

솔라나에는 주소(Address)와 계정(Account)이라는 두 가지 개념이 있습니다.

솔라나 네트워크의 상태(State)는 개념적으로 Key-Value 형태로 관리됩니다. 여기서 Key는 계정을 식별하는 Address이고, Value는 해당 주소에 연결된 Account 객체입니다.

즉, 솔라나에서는 주소를 통해 계정을 조회하고, 계정에 저장된 잔액이나 데이터를 확인할 수 있습니다.

계정 주소(Address)

https://solana.com/ko/docs/core/accounts/account-structure

https://solana.com/ko/docs/core/accounts/account-structure

Address는 계정을 식별하기 위한 고유한 32바이트 값입니다. 일반적으로 사람이 읽을 수 있도록 Base58로 인코딩한 문자열로 표현합니다. 주소는 생성 방식에 따라 크게 두 가지로 구분할 수 있습니다.

공개키 주소(Public Key Address)

Ed25519 키페어의 공개키를 계정 주소로 사용합니다. 대응하는 개인키를 통해 서명할 수 있습니다.

다음 예제는 Solana SDK를 사용하여 keypair를 생성합니다.
import { generateKeyPairSigner } from "@solana/kit";

// Kit does not enable extractable private keys
const keypairSigner = await generateKeyPairSigner();
console.log(keypairSigner);

PDA(Program Derived Address)

Program ID와 Seed 등을 이용해 결정론적으로 생성한 주소입니다. 대응하는 개인키가 없으며, 해당 PDA를 파생한 프로그램이 런타임을 통해 서명 권한을 행사할 수 있습니다.

다음 예제는 Solana SDK를 사용하여 PDA를 파생합니다.
import { Address, getProgramDerivedAddress } from "@solana/kit";

const programAddress = "11111111111111111111111111111111" as Address;

const seeds = ["helloWorld"];
const [pda, bump] = await getProgramDerivedAddress({
  programAddress,
  seeds
});

console.log(`PDA: ${pda}`);
console.log(`Bump: ${bump}`);

다만 주소가 생성되었다고 해서 온체인에 Account가 반드시 존재하는 것은 아닙니다. 주소를 미리 생성하거나 계산한 뒤, 실제 Account는 별도로 생성할 수 있습니다.

계정(Account)

Account는 SOL 잔액과 데이터 등을 보유하는 객체입니다. 모든 Account는 다음 5가지 필드로 구성됩니다.

필드 타입 설명
lamports u64 lamport 단위의 잔액. 소유자는 차감 가능하며, 모든 프로그램은 입금 가능합니다.
data Vec<u8> 계정 상태 또는 프로그램 바이트코드. 최대 10 MiB. 소유자만 쓰기 가능합니다.
owner Pubkey 쓰기 권한을 가진 프로그램. 데이터가 0으로 초기화된 경우에만 재할당 가능합니다.
executable bool True = 프로그램 계정입니다.
rent_epoch Epoch 더 이상 사용되지 않습니다. rent 면제 계정의 경우 u64::MAX로 설정됩니다.
_Example JSON_
// Example Token Mint Account
Account {
    lamports: 1461600,
    data.len: 82,
    owner: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb,
    executable: false,
    rent_epoch: 0,
    data: 010000001e213c90625a7e643d9555bb01b6c3fe6416d7afd523ce8c7ddd9b923ceafb9d00000000000000000901010000001e213c90625a7e643d9555bb01b6,
}

// Example Token Program Account
Account {
    lamports: 4513200894,
    data.len: 134080,
    owner: BPFLoader2111111111111111111111111111111111,
    executable: true,
    rent_epoch: 18446744073709551615,
    data: 7f454c460201010000000000000000000300f70001000000d8f90000000000004000000000000000800902000000000000000000400038000400400009000800,
}

Lamport

lamports 필드는 계정의 잔액을 lamport 단위로 보유합니다 (1 SOL = 1,000,000,000 lamport). 모든 계정은 온체인에 데이터를 저장하기 위해 최소 lamport 잔액을 유지해야 합니다. 환불 가능한 이 스토리지 잔액은 계정의 데이터 크기에 비례합니다.

Data

data 필드는 임의의 바이트 배열을 저장합니다. 그 내용은 계정 유형에 따라 다릅니다:

  • Program Account: 실행 가능한 바이트코드 또는 바이트코드를 저장하는 프로그램 데이터 계정의 주소를 포함합니다.
  • Data Account: 프로그램 정의 상태 데이터를 포함합니다. 소유 프로그램이 직렬화 형식을 정의합니다.

각 계정에 대해서는 아래에서 자세하게 다뤄보겠습니다.

owner

owner 필드는 이 계정을 소유한 프로그램의 프로그램 ID(공개 키)를 포함합니다. 소유자 프로그램만이 다음을 수행할 수 있습니다:

  • 계정의 data 필드 수정
  • 계정의 lamports 필드에서 램포트 차감

모든 프로그램은 다른 계정에 램포트를 추가할 수 있지만, 소유자만이 계정에서 램포트를 차감할 수 있습니다.

Owner와 Address는 무슨차이야?

  • Address: 해당 계정을 식별하는 고유 주소입니다.
  • Owner: 해당 계정의 데이터를 수정할 권한을 가진 프로그램의 주소(Program ID)입니다.

address와 owner는 서로 다른 개념입니다.

Owner는 모든 Account에 있어야 해?

네. 솔라나의 모든 온체인 Account에는 반드시 owner 필드가 존재하며, owner는 null일 수 없습니다. 반드시 유효한 32바이트 Program ID가 있어야 합니다.

따로 설정을 안하면?

기본적으로 일반 사용자 지갑은 System Program이 owner입니다. SOL을 전송할 수 있으며, owner의 프로그램에 따라 차감 방식이 달라집니다.

솔라나에는 중요한 규칙이 있습니다. Account의 lamports 잔액을 차감할 수 있는 프로그램은 해당 Account의 owner뿐입니다.

실행 과정은 다음과 같습니다.

  1. 사용자 A가 개인키로 트랜잭션에 서명합니다.
  2. System Program의 Transfer 명령을 호출합니다.
  3. System Program이 A 계정에서 1 SOL을 차감하고 B 계정에 추가합니다.

즉, 사용자 개인키의 서명은 전송을 승인하는 역할이고, 실제 lamports 변경은 System Program이 수행합니다.

executable

executable 필드는 계정이 프로그램 계정인지 데이터 계정인지를 결정하는 불리언 값입니다:

  • true = 프로그램 계정(실행 가능한 코드 포함)
  • false = 데이터 계정(상태 저장)

Rent epoch

rent_epoch 필드는 더 이상 사용되지 않습니다.

2. Solana의 Program과 Program ID

솔라나에서 Program은 이더리움의 스마트 컨트랙트와 유사한 개념입니다. 프로그램은 특정 기능을 수행하도록 작성된 실행 코드이며, 일반적으로 Rust로 개발합니다. 예를 들어 SOL 전송, 토큰 발행, 토큰 잔액 변경, 사용자 상태 관리 등의 작업을 수행할 수 있습니다.

프로그램은 자신이 소유한 Account의 데이터를 수정할 수 있는 권한을 가집니다. 다만 모든 프로그램이 모든 Account를 임의로 수정할 수 있는 것은 아닙니다. 각 Account의 owner에는 해당 계정을 관리하는 프로그램의 ID가 기록되며, 런타임은 이 정보를 바탕으로 데이터 수정 권한을 검사합니다.

Program ID란?

Program ID는 프로그램을 식별하는 고유한 주소입니다. 일반적인 Account 주소와 마찬가지로 32바이트 값이며, Base58 문자열로 표현합니다.

대표적인 프로그램은 다음과 같습니다.

프로그램 Program ID
System Program 11111111111111111111111111111111
Token Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
Token-2022 Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

이 중 Token, BPFLoader처럼 의미 있는 문자가 포함된 주소는 사람이 쉽게 구분하도록 만들어진 주소입니다. 해당 문자열 자체가 별도의 계정 유형이나 메타데이터를 나타내는 것은 아닙니다.

3. Solana의 Account 유형

솔라나의 Account는 executable 필드에 따라 크게 두 가지로 구분할 수 있습니다.

  • Program Account: executable = true
  • Data Account: executable = false

Program Account는 실행 가능한 프로그램과 관련된 계정이며, Data Account는 프로그램이 사용하는 상태나 사용자 데이터를 저장하는 계정입니다.

이더리움에서는 스마트 컨트랙트 계정이 코드와 상태 데이터를 함께 관리하지만, 솔라나는 실행 코드와 상태 데이터를 별도 계정으로 분리하는 구조를 사용합니다.

3.1. Program Account

Program Account는 실행 가능한 프로그램을 나타내는 계정입니다. 솔라나에서 프로그램을 배포하면 해당 프로그램을 식별하는 Program Account가 생성되며, executable 필드는 true로 설정됩니다.

Program Account의 owner에는 해당 프로그램을 로드하고 관리하는 Loader Program의 ID가 저장됩니다.

Loader는 프로그램을 로드하고 실행할 수 있도록 관리하는 프로그램입니다. 대표적으로 BPF Loader 계열이 있습니다.

Program Account의 실행 코드 저장 방식은 어떤 Loader를 사용하는지에 따라 달라집니다. 일부 Loader에서는 Program Account의 data 필드에 실행 가능한 바이트코드를 직접 저장합니다. 반면 Loader-v3(Upgradeable Loader)를 사용하는 경우, 실제 실행 코드를 별도의 Program Data Account에 저장합니다.

Program Data Account

Program Data Account는 Loader-v3를 사용하는 프로그램의 실제 실행 코드(sBPF 바이트코드)를 저장하는 계정입니다. Loader-v3에서는 Program Account의 data에 Program Data Account를 참조하는 정보가 저장되고, Program Data Account의 data에는 실제 실행 코드가 저장됩니다.

즉, 프로그램 실행을 위해 Program Account를 참조하면 런타임은 해당 Program Account의 프로그램 실행 코드를 로드하며, Loader-v3 구조에서는 Program Data Account에 저장된 바이트코드를 사용합니다.

Program Data Account는 Program Account와 별도의 주소를 가진 독립적인 계정입니다. Program Data Account 자체는 executable = false이므로, 큰 분류에서는 Data Account에 포함됩니다.

3.2. Data Account

Data Account는 실행 가능한 코드를 포함하지 않는 계정으로, executable 필드가 false인 계정을 의미합니다. 주로 프로그램이 실행하는 데 필요한 데이터나 실행 과정에서 생성·변경되는 상태 데이터를 저장합니다.

Data Account는 역할에 따라 여러 종류로 나눌 수 있으며, 공식 문서에서는 Program State Account, System Account, Sysvar Account 등을 설명합니다.

Program State Account

Program State Account는 프로그램이 사용하는 상태 데이터를 저장하는 계정입니다.

예를 들어 포인트 관리 프로그램을 개발했다고 가정하겠습니다. Program Account에는 포인트를 적립하거나 차감하는 로직이 존재하고, Program State Account에는 사용자별 포인트 잔액이 저장됩니다.

즉, Program Account의 실행 코드 자체를 저장하는 것이 아니라, 프로그램이 관리하는 애플리케이션의 상태를 저장하는 계정입니다.

Program State Account를 생성하는 과정은 일반적으로 두 단계로 이루어집니다.

  1. System Program을 호출하여 새로운 Data Account를 생성하고, owner를 해당 프로그램의 Program ID로 지정합니다.
  2. 계정의 소유 프로그램을 호출해 생성된 Account의 data 필드를 초기화합니다.

하나의 프로그램은 여러 Program State Account를 관리할 수 있습니다. 이 계정은 Keypair의 공개키 주소 또는 PDA를 이용해 생성할 수 있습니다.

System Account

System Account는 System Program이 소유하는 계정입니다. 여기서 System Program과 System Account는 서로 다른 개념입니다.

  • System Program: 계정 생성, SOL 전송, 공간 할당, 소유권 지정 등의 기능을 제공하는 내장 프로그램
  • System Account: owner가 System Program으로 설정된 일반적인 계정

예를 들어 사용자가 SOL을 보유하는 일반 지갑 계정은 System Account에 해당합니다.

owner 필드는 11111111111111111111111111111111(System Program)입니다.

Sysvar Account

Sysvar Account는 솔라나 네트워크의 공통 상태 정보를 제공하는 특수한 계정입니다. 일반적인 프로그램이 임의로 수정하는 데이터 계정과 달리, 런타임이 관리하는 네트워크 정보를 제공합니다.

대표적으로 Clock Sysvar는 현재 Slot, Epoch, Unix Timestamp 등의 정보를 제공하며, Rent Sysvar는 Rent와 관련된 파라미터를 제공합니다.

계정 리스트 링크

Sysvar Account 역시 executable = false인 Data Account에 속합니다.

Solana Account 유형 정리

지금까지 살펴본 계정 유형을 정리하면 다음과 같습니다.

구분 executable 목적 owner
Program Account true 프로그램 실행 진입점 Loader Program
Program Data Account false Loader-v3 프로그램의 실행 코드 저장 BPF Upgradeable Loader
Program State Account false 프로그램의 상태 데이터 저장 해당 프로그램
System Account false SOL 잔액 보유 등 System Program
Sysvar Account false 네트워크 공통 상태 정보 제공 Sysvar 관련 프로그램

여기서 Program Account와 Data Account는 executable 여부에 따른 큰 분류입니다.