Libra — 핵심 모듈 분석 : LibraAccount

Seungwon Go
Jul 1 · 9 min read

By Seungwon Go, CEO & Founder at ReturnValues (seungwon.go@returnvalues.com)

우리는 앞서 Move 언어에 대한 기초적인 지식을 습득하였습니다.

리브라 코어를 설치하고 나면, libra 폴더 밑에 language 폴더를 확인할 수 있습니다. 이중 여러분이 리브라 Move 언어를 학습하면서 가장 많이 접하게 되는 standard library (stdlib)를 먼저 살펴 보려고 합니다.

standard library 에는 Move IR로 작성된 코어 모듈이 5가지(hash, libra_account, libra_coin, signature, validator_set)가 있는데, 이번 시간에는 LibraAccount 모듈 소스 코드를 살펴보고자 합니다.

LibraAccount 코드를 가장 먼저 살펴보는 이유는, 현재 리브라 테스트넷에서는 peer-to-peer transfer, account creation, minting new Libra, key rotation 이렇게 총 4가지 transaction script만을 지원합니다. 이 4가지 transaction script 모두 내부적으로는 LibraAccount를 사용하게 되어 있습니다.

libra_account.mvir

파일의 확장자가 .mvir 로 되어 있습니다. 눈치 채셨겠지만, mvir은 Move IR의 약자입니다. libra_account.mvir은 아래와 같은 public 함수를 제공합니다.

Public 함수

  • deposit(payee: address, to_deposit: R#LibraCoin.T) : 리브라 코인을 특정 address로 보내는 함수입니다.
  • mint_to_address(payee: address, amount: u64) : 입력된 address로 입력된 amount 만큼 추가 발행합니다. 이 함수는 public으로 선언되어 있지만, 아무나 실행할 수는 없고, libra association에서 권한을 준 사람만 실행할 수있다고 나와 있습니다.
  • withdraw_from_sender(amount: u64) : sender의 계좌에서 입력된 리브라 코인 만큼 인출합니다.
  • pay_from_sender(payee: address, amount: u64) : sender의 계좌에서 payee로 입력된 amount 만큼의 리브라 코인을 전송합니다. 이때 payee에 해당하는 account가 존재하지 않는다면, 자동으로 전송된 리브라 코인 만큼을 갖는 account를 생성해 줍니다.
  • rotate_authentication_key(new_authentication_key: bytearray) : 트랜잭션을 실행시킨 sender의 authentication key를 새로운 키로
  • create_new_account(fresh_address: address, initial_balance: u64) : 새로운 account를 생성하고, 초기 balance를 설정합니다.
  • balance(addr: address) : 입력된 address의 잔액을 반환합니다.
  • sequence_number(addr: address) : 입력된 address의 sequence number를 반환합니다.
  • exists(check_addr: address) : 입력된 address가 실제 존재하는 address인지 알려줍니다.

이 중에 가장 간단해 보이는 exists 함수를 통해 Move 내에서는 함수 구조에 대해서 살펴 보도록 하겠습니다.

public exists(check_addr: address): bool {    let is_present: bool;    is_present = exists<T>(move(check_addr));    return move(is_present);}

아마 이글을 읽고 계신 분들은 대다수가 개발에 대한 경험을 가지고 있는 분일거라 생각합니다. 맞습니다. 여러분이 지금 생각하시는 것 처럼 exists 함수는 파라미터로 입력된 주소가 실제 존재하는 주소인지 체크해주는 함수입니다.

여기서 가장 기본이 되는 Move 언어의 함수 기본 형식을 보면,

public exists(check_addr: address): bool

외부에서 transaction script를 이용해서 호출 가능한 함수의 경우는 위에서 보시는 것 처럼 public 이라고 선언이 되어 있습니다. 함수명은 exists 입니다.

public exists(check_addr: address): bool

그리고 파라미터는 파라미터를 받는 변수명이 먼저 나오고, 변수 타입을 선언하도록 되어 있습니다.

마지막으로 return 이 있는 경우에는 return 타입을 선언하게 되어 있습니다.

public exists(check_addr: address): bool

Move에서 함수는 어떤 형식으로 선언이 되는지 이제 감을 잡으셨을 것입니다.

그리고 libra_account.mvir 코드의 제일 아래를 보시면 좀 생소한 아래의 2개의 함수를 발견할 수 있습니다.

prologue()

모든 트랜잭션 실행전에 먼저 실행이 되어서, 기본 검증을 사전에 수행하는 함수입니다.

  • 트랜잭션을 실행한 account의 auth key와 트랜잭션의 public key가 매칭이 되는지 검증합니다.
  • 트랜잭션을 실행할 충분한 balance가 있는지 확인합니다. (트랜잭션을 실행하기 위해서는 gas비가 필요합니다.)
  • account의 sequence 번호가 트랜잭션의 sequnce 번호 보다 같거나 작은지 검증합니다.

epilogue()

모든 트랜잭션 실행 후에 실행이 되는 함수 입니다.

  • 트랜잭션을 실행하면서 사용된 gas 금액을 계산해서 sender의 계좌에서 해당 gas 금액만큼 소각 시킵니다.
  • sender의 sequence number를 1 증가 시킵니다.

무슨 드라마, 소설도 아닌데 프롤로그, 에필로그가 있습니다. 참 함수명을 드라마틱하게 정한것 같네요^^

Resource

자 다시 소스 코드로 돌아가서 LibraAccount 코드에서 가장 먼저 나오는 즉, LibraAccount에 대한 리소스를 선언하는 부분을 보도록 하겠습니다. Move 언어의 가장 큰 특징 중 하나가 이렇게 Resource를 정의해서 사용할 수 있다는 것입니다. 아래에서 보시는 것 처럼, balance, authentication_key, sequence_number, set_events_couint, received_events_count 데이터를 저장 할 수 있는 구조로 되어 있습니다.

// Every Libra account has a LibraLibraAccount.T resourceresource T {    balance: R#LibraCoin.T,    authentication_key: bytearray,    sequence_number: u64,    sent_events_count: u64,    received_events_count: u64}

LibraCoin.T 리소스에 대해서는 Move 언어를 공부하시면서 굉장히 자주 보게 될것입니다. 위에서 보시는것 처럼 쉽게 생각하시면 데이터 구조체라고 보실 수 도 있지만, 리소스로 정의함으로써 가지게 되는 다른 강력한 기능이 존재합니다.

  • Move 언어의 중요 특징 중 하나는 커스텀 리소트 타입을 정의 할 수 있다는 것입니다.
  • 리소스는 데이터 구조로 저장되거나, 프로시저에 파라미터로 전달되거나, 프로 시저로 부터 반환 될 수 있습니다.
  • Move 리소스는 절대 복사되거나, 재사용되거나, 삭제되거나 하지 않습니다. 리소스 타입은 타입을 정의한 모듈에서만 생성하거나, 삭제할 수 있습니다. Move VM은 bytecode verifier를 통과하지 않은 코드는 실행하지 않습니다.
  • 리브라 currency는 LibraCoin.T.라는 리소스 타입으로 구현됩니다.

Event

그리고 트랜잭션을 통해 발생하는 이벤트를 전달하기 위해서 아래와 같이 2가지의 이벤트 전송을 위한 struct가 정의 되어 있습니다.

// Message for sent eventsstruct SentPaymentEvent {   payee: address,   amount: u64,}// Message for received eventsstruct ReceivedPaymentEvent {    payer: address,    amount: u64,}

실제 코드 상에서는 deposit 함수가 실행이 될때, SentPaymentEvent와 ReceivedPayementEvent가 호출이 됩니다.


지금까지 LibraAccount에 대한 간략한 분석을 진행해 보았습니다.

앞에서 간략하게 정리했던 각 public 함수에 대한 내부 코드를 하나 하나 설명하는 작업은 이 글을 통해서는 진행하지 않도록 하겠습니다. 실제 각 함수별 코드를 보시면, 상당히 구체적으로 주석이 달려 있어서 각 코드가 어떤 역할을 수행하는지 명확히 이해를 하실 수 있습니다. 단지 Move 언어 문법에 대한 익숙함이 없을 뿐인데, 이 부분은 지속적으로 시간을 갖고 코드를 보시는 수 밖에 없는 것 같습니다. 저도 코드를 계속 보다 보니, 이제야 눈에 익숙해 지고 있는 것 같습니다. 다음에는 LibraAccount와 더불어 가장 중요한 모듈인 LibraCoin에 대해서 분석하는 글을 작성하도록 하겠습니다.

ReturnValues

ReturnValues Blogs

Seungwon Go

Written by

Founder & CEO at ReturnValues

ReturnValues

ReturnValues Blogs

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade