Libra — 핵심 모듈 분석 : LibraAccount

Seungwon Go
ReturnValues
Published in
9 min readJul 1, 2019

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에 대해서 분석하는 글을 작성하도록 하겠습니다.

--

--