DSRV
Published in

DSRV

[KO] 솔라나에서의 NFT 구현 1편: 솔라나 기본 개념 이해하기 — 컨트랙트와 스토리지의 구조

솔라나(Solana)의 등장

1. 계정(Accounts)

Program Account는 Data Account에 저장된 State를 가져와 실행합니다.
[그림 1–1] Program Account는 Data Account에 저장된 State를 가져와 실행합니다.[1]

1.1. Program Account

솔라나는 이더리움의 컨트랙트와 다르게, 프로그램(컨트랙트)이 상태를 저장하지 않습니다.
[좌: 그림 1–2] 이더리움의 컨트랙트(Contract) ㅤ [우: 그림 1–3] 솔라나의 프로그램(Program), 출처: Banksea Finance Medium
💡 DSRV’s Tip: Loader란?솔라나 공식문서에서 Loader는 “A program with the ability to interpret the binary encoding of other on-chain programs.”[2] 라고 정의합니다.프로그램의 배포란, 쉽게 얘기하자면 Program Account의 데이터에 코드를 업로드하는 과정입니다.
클라이언트가 프로그램 데이터를 serialize하여, write instruction에 담아 로더에 보내면, 로더는 instruction에 담겨있는 데이터를 Program account에 쓰고, 해당 account를 executable로 마킹합니다.
즉, 로더란 프로그램의 배포와 실행을 수행하는 Native Program 입니다.
로더의 종류에는 BPF Loader 2, BPF Upgradeable Loader, Native Loader 등이 있습니다. [3][4][5]
- BPF Loader 2: BPF Program의 loading, finalizing, executing을 수행합니다. [6]
- BPF Upgradeable Loader: BPF Program의 deploying, upgrading, executing을 수행합니다. [7]
- Native Loader: Native program의 loading, executing을 수행합니다.
참고로, `solana deploy` 명령어를 사용하여 프로그램을 배포하면, BPF Loader 2를 사용하며, 프로그램은 수정 불가능합니다.
`solana program deploy` 명령어를 사용하여 프로그램을 배포하면, BPF Upgradeable Loader 를 사용하며, 프로그램은 수정 가능합니다.
따라서 Ownership의 관계는 아래와 같습니다. 여기서 Ownership이라 함은 쉽게 설명하자면 Account의 쓰기 권한을 의미합니다. 아래 관계에서 왼쪽 Account에 대한 쓰기 권한은 오른쪽 Account에 있습니다.- Data Account → custom program (ex. Candy Machine, Token Program)
- Upgradeable custom program (ex. Candy Machine) → BPF Upgradeable Loader
- Immutable custom program (ex. Token Program) → BPF Loader
- Native program [8] (ex. BPF Loader) → Native Loader
💡 DSRV’s Tip: Executable Account에 딥 다이브해보기

솔라나의 Contract 런타임 환경인 Sealevel에서 Executable Accounts는 수정 불가능(immutable)하게 데이터를 저장합니다.
다만 프로그램이 수정 가능(mutable)하게 설정되면, 저장된 실행 코드가 나중에 수정할 수 있어야 하는데요,
그 과정에 대해선 아래에 설명하겠습니다.

1) `solana deploy` 명령어로 배포한 경우 (immutable program accounts)
Sealevel의 executable account에 바로 실행 코드가 byte code 형태로 저장됩니다.
저장된 실행 코드 데이터는 immutable하기 때문에, Program Account를 업데이트 할 수 없습니다.

2) `solana program deploy` 명령어로 배포한 경우 (mutable program accounts)
Sealevel의 executable account에는 실행 코드가 아닌 프록시(proxy) 주소가 저장됩니다.

해당 프록시 주소는 mutable account를 가리키며, 해당 계정 (mutable account)에 실행 코드가 byte code 형태로 저장됩니다. Program Account를 업데이트하고자 하면, 위 mutable account에 저장된 실행 코드가 수정되지 때문에, 수정 가능합니다.

1.2. Data Account

1.3. Native Account

2. Transactions & Instructions

트랜잭션은 서명과 메세지로 구성되며, 메세지는 다시 인스트럭션들의 집합으로, 인스트럭션들은 이 인스트럭션을 수행할 program Id와 어카운트 주소, 데이터로 구성됩니다.
[그림 1–4] 트랜잭션 도표, 출처 @c0wjay, DSRV
프로그램은 인스트럭션을 받은 후, 내부에서 실행하기 위해 역직렬화 과정을 거치며, 다시 직렬화 과정을 거쳐 인스트럭션을 생성합니다.
[그림 1–6] Instruction의 serialization & deserialization 출처: 솔라나 쿡북
💡 DSRV’s Tip: Serde에 대해 알아보기

위 설명에 대한 코드 수준의 이해는 솔라나 쿡북을 참고하면 좋습니다. [9]
짧게 Serialization 과 Deserialization 에 대해 설명해보도록 하겠습니다.

전반적인 프로세스는 위 도표(그림 1-6)와 같은데, 클라이언트 ↔ 프로그램 또는 프로그램 ↔ 프로그램 간의 RPC 통신은 Serialized 된 byte stream으로 주고 받습니다. 프로그램은 받은 byte의 flag에 따라 Instruction의 종류를 분류하고, Payload를 Deserialize하는 과정을 거칩니다.
Borsh Deserialize 과정 살펴보기위와 같은 코드를 실행하면, 다음과 같은 결과가 출력됩니다. Primitive(255, 65535, 4294967295, "hello", "world", [1, 2, 3, 4, 5], {"cookbook": "recipe", "recipe": "ingredient"}) 이는 byte array인 prim을 Primitive struct로 deserialize하는 과정의 예시입니다.  어떻게 대응되는 지는 아래 도표를 참고해주세요.
Borsh Deserialization 모식도
[그림 1–7] Borsh Deserialization 예시, 출처 @c0wjay

3. Token Program

Mint Account 관계도
[그림 1–8] Mint Account, 출처: Twitter, @pencilflip
Token Account 관계도
[그림 1–9] Token Account, 출처: Twitter, @pencilflip
[그림 1–10] 여러 유저들의 Token 구조, 출처: Twitter, @pencilflip

4. Program Derived Address & Cross-Program Invocation

용어 정리: PDA란?본 글에서 PDA는 Program-Derived Address를 칭하고, 해당 Program-Derived Address를 주소로 하는 계정을 PDA Account라 칭합니다.
💡 DSRV’s Tip: 재진입 공격(Re-entrancy Attack)에 대해 알아보기재진입 공격(Re-entrancy Attack)이란, 컨트랙트 A가 외부 컨트랙트 B를 호출하는 구조에서,
컨트랙트 A의 실행이 완료되기 전에, 컨트랙트 B에서 다시 컨트랙트 A를 재귀적으로 호출하여 컨트랙트 A의 금액을 모두 인출하는 공격입니다.
예를 들자면, 아래와 같은 로직으로 공격이 수행됩니다. ([참고 예제](<https://solidity-by-example.org/hacks/re-entrancy>))1. 컨트랙트 A 에 해커의 돈이 100원이 있고, 이를 돌려받고자 한다.
2. 돌려받는 주소를 해커가 작성한 컨트랙트 B로 한다.
3. 컨트랙트 B에는 fallback function(돈이 입금되면 실행되는 함수)이 있고, 이 함수는 다시 컨트랙트 A를 호출하여 돈을 돌려받는 과정을 재실행한다.
4. 아직 컨트랙트 A의 실행은 완료되지 않았으므로(즉 컨트랙트 A에는 해커의 돈이 100원 그대로 있다고 상태가 저장되어 있으므로), 다시 100원의 송금이 실행된다.
5. 이는 다시 재귀 호출을 만들어, 계속해서 컨트랙트 A에서 B로 100원씩 송금이 실행된다.
💡 DSRV’s Tip: 왜 Program-Derived Address(PDA)가 필요하나요?

아래와 같은 중고거래 예시를 가정하여 설명해보겠습니다.

"밥이 앨리스한테 물건을 보내고, 돈을 받는다"


그렇다면 밥 입장에서는 물건을 보내기 전에, 앨리스가 돈을 주겠다는 확약을 받아야하고, 앨리스는 정상적인 물건인지 확인 전까지는 돈을 보내주기가 꺼려집니다.
이 때 3자에게 돈을 맡겨서 물건의 배송이 완료된 후에, 돈을 보내는 구조로 처리하는 것이 일반적입니다.

온라인 상의 거래도 항상 양쪽이 모두 온라인인 상황에서만 거래되는 것이 아니라, 한쪽이 오프라인이어도 거래가 진행되는 상황이 있을 수 있습니다. 이 때 제 3자가 돈을 보관해야하는데, 솔라나에선 프로그램이 그 역할을 담당합니다. 문제는 그 프로그램이 특정 유저에 의해 작동이 된다면, 매 거래시마다 유저가 서명해야 한다는 문제가 생기고, 또한 그 유저가 나쁜 마음을 먹게 된다면 악의적으로 거래가 이루어질 수 있습니다. 예를 들면, 위에서 앨리스에게 뒷돈을 받아 밥에게 송금을 취소하는 경우가 있을 것입니다.

그렇다고 프로그램이 프로그램 소유의 개인키를 갖고 있기에는, 오픈소스 코드라면 개인키가 공개되거나 탈취될 수 있다는 문제점을 갖고 있습니다. 이러한 문제인식에서 PDA라는 개념이 나오게 되었고, 개인키가 존재하지 않으면서도 “프로그램”만 사용할 수 있고, 이를 상호 검증할 수 있는 서명(Signature)의 개념이 생긴 것입니다.

5. Wallet

[그림 1–10] 여러 유저들의 Token 구조, 출처: Twitter, @pencilflip

6. NFT in Solana

6.1. NFT의 메타데이터

6.2. NFT의 발행 프로세스

NFT 어카운트들의 관계도
[그림 1–11] NFT account들의 구조, 출처: Twitter, @pencilflip

글을 마무리하며

--

--

[DSRV’s Official Publication on Medium | DSRV 공식 미디엄 채널입니다] “Onboarding the World to Web3”

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store