나의 WEB3 여정을 살펴보면, 처음에는 알트 코인 투자로 시작했다가 작년부터 기술에 대한 본격적인 관심을 가지게 되어서 여러 블록체인 해커톤의 참여 및 저서 활동까지 이어져 왔다. 불과 2년전까지만 해도 송도에 있는 제약회사에서 AI 모델을 개발하고 있었는데 지금은 언리얼 게임 개발에 블록체인 모듈을 통합하는 작업을 하고 있다는 사실이 신기하기만 하다.

각설하고, 내가 개발하고 있는 TPS 게임에 NFT minting 기능을 붙이기 위해 언리얼 플러그인을 알아보던 도중 3S Game Studio라는 회사의 제품을 발견하게 되었다. 기본적인 기능들은 블루프린트 모듈로 제공하고 있었고 Ethereum 관련 네트워크에서 getBalance, getTransactionCount, SignTransaction, SendRawTransaction와 같은 함수들을 추가적인 RPC 설정 없이 편하게 쓸 수 있게 제공해주었다. 내가 만드는 게임에 WEB3 기능을 얹을 수 있다는 부푼 기대감에 바로 작업을 시작하였다.


Unreal에서 블록체인 접근하기

이번 개발에서 최종 목표는 NFT의 민팅이고 UI 제작도 플레이어가 가지고 있는 NFT의 목록을 인벤토리 형식으로 보여줄 계획이다. 하지만, 그전에 뭐니뭐니해도 WEB3 프로젝트에서 가장 먼저 시도해 봐야할 기본적인 지갑 기능을 구현해보았다.

개인 주소 세팅

RPC 연결

만약 개인이 자신의 컴퓨터에서 블록체인 노드를 돌린다면 외부에서 확인할 필요 없이 노드를 통해서 블록체인의 상태를 체크할 수 있다. 하지만, 대부분의 사람들이 그렇지 않으므로 대신 노드를 돌려주고 블록체인의 상태를 확인할 수 있게 제공하는 서비스 업체들이 있다.

이들을 Blockchain RPC Node Provider라고 불리는데 RPC Endpoint에 해당하는 Url을 제공하고 자신이 블록체인에 query하고 싶은 함수를 Json 형식으로 request하면 원하는 정보를 response로 돌려준다.

RPC Node Provider 들간의 속도 비교

이미 시중에는 다양한 플레이어들이 있다. 필자는 RPC Node ProviderAlchemy, Infura, Quicknode를 사용해보았는데 개인적으로 Alchemy가 속도적인 면과 사용성이 좋아서 이번 프로젝트에서도 사용하기로 하였다.

Alchemy에서의 개인 project 화면. 연동하고 싶은 network를 추가할 수 있다.

한 가지 주의하여야 할 점은 하나의 RPC Url로 각기 다른 블록체인을 접근할 수 있는 것은 아니다. 블록체인은 각각의 네트워크마다 폐쇄된 환경이라는 사실을 명심하자. 자신이 만드는 서비스에서 여러 네트워크에 접근하고 싶다면 RPC provider에서 접근하고 싶은 네크워크의 개수만큼 개별 RPC-Url이 필요하다. 필자는 Ethereum Mainnet, Ethereum Testnet(Sepolia와 그외), Polygon zkEVM, Polygon PoS를 사용하기로 하였다.

기존 프로젝트에 붙이는 WEB3 모듈의 최종 기능은 OpenseaNFT를 민팅하는 것인데, Opensea에서 Sepolia testnet을 지원해준다는 사실을 알았다. 우선, Sepolia Network에서 충분한 테스트 과정을 거친후 Ethereum보다 상대적으로 가스비가 싼 Polygon networkNFT를 민팅할 계획이다.

플러그인에서 Get Blockchain Configuration이라는 함수를 통해 다양한 네트워크에 대한 Blockchain Configuration을 만들 수 있다. 하지만, 테스트 결과 Mainnet 쪽은 문제가 없었지만 Ethereum Testnet에서 query가 안되서 결국 직접 RPC-endpoint를 만들어 네트워크 별 Blockchain Configuration을 만들기로 하였다.

플러그인에서 지원하고 있는 다양한 네트워크들

직접 RPC를 연결해서 만든 네트워크 별 Blockchain Config

공개 주소 만들기

RPC 작업이 완료했으니 이제 WEB3 지갑 주소를 만들 차례이다. 지갑 주소에 대해서도 할 말이 굉장히 많지만 여기서 다룰 주제는 아닌 듯 하여 이더리움의 accounting system은 비트코인처럼 UTXO 기반이 아닌 Ledger 기반이라는 점과 공개 주소를 위해서는 해시 함수타원 곡선 알고리즘이 쓰인다는 점만 간단하게 알고 넘어가자.

이더리움의 Ledger 기반은 우리가 평소에 회계처리를 할때 사용하는 장부 기반이다. A가 2ETH, B가 1ETH를 소유하고 있는 상태에서 A가 B에게 1ETH를 보내면 A는 1ETH, B는 2ETH가 장부에 적히는 것이다. 비트코인의 UTXO 시스템은 아주 독특한 시스템인데 누가 누구에서 전송했다는 사실 하나하나씩 모아둔다. 이들은 당사자가 코인 전송을 하기 전까지 Unspent 상태가 되었다가 전송을 할때 Spent 상태로 만들면서 그 사실을 제거해버린다. 짧게 설명하기에는 조금 복잡한 내용이므로 더 자세한 내용은 여기를 참고해보자.

이제, 우리가 이번 프로젝트에서 직접적으로 다룰 이더리움의 키 생성에 대해서 알아보자. 이더리움의 공개 주소가 만들어지는 과정은 비트코인의 공개 주소가 만들어지는 원리와 매우 비슷하다. 먼저 Random Seed를 사용한 해시 함수(비트코인은 SHA-256, 이더리움은 secp256k1)를 사용해 256bit의 Private key를 만든다.

그런 다음, 이 Private keyECDSA(타원 곡선 디지털 서명 알고리즘)에 넣게 되는데 이 알고리즘도 input에 대해 output이 deterministic하지만 output을 가지고 intput을 역방향 추론할 수 없다는 점에서 해시 함수와 비슷한 면을 가지고 있다.

타원 곡선 알고리즘

Private key를 타원 곡선 연산을 하게 되면 그에 해당하는 Public key를 얻게 된다. 하지만, 이 값은 주소로 쓰기에는 너무 긺으로 여기서 bit shifting을 통해 자릿수를 줄여 최종 주소를 만들어낸다.

BP에서 키 생성 함수 플로우

이를 Unreal Blueprint에서 구현하면 다음과 같은 플로우를 가진다. 처음에 Generate Private Key를 통해 키를 생성, 이 Private key를 가지고 Generate Public KeyPublic Key를 생성, 최종적으로 Generate Ethereum Address from Public Key(Array of bytes)를 통해 주소를 얻는다.

한번 생성한 Private Key는 Unreal에서 제공하는 SaveGame Object에 저장해 게임이 꺼져도 ~Saved/SaveGames 경로에 binary 파일로 저장되게 된다. Save Game to Slot, Load Game to Slot 등의 함수를 사용해서 private key를 매번 생성하지 않게 한다.

WEB3 지갑 기능 붙이기

주소를 생성했다면 이제 주소를 통해 블록체인과 Interaction하는 부분을 만들어보자. WEB3 지갑을 제작하는데에 있어서 핵심 기능은 지갑이 연결되어 있는 네트워크별 잔고 표시토큰 송금 및 수신기능이므로 이 부분을 작업할 것이다.

MetaMask의 Wallet UI

WEB3 지갑이 무엇인지 모르는 분들을 위해 가장 유명한 WEB3 지갑인 MetaMask wallet UI를 가져왔다. 개인적으로 MetaMask의 깔끔한 디자인이 정말 마음에 든다. MetaMask네트워크별 잔고 표시토큰 송금 및 수신기능 외에도 유동성 풀을 통해 토큰을 바꾸는 스왑기능, 서로 다른 네트워크 간에도 토큰을 전송할 수 있는 브리지 기능과 해당 wallet이 선택된 네트워크 상에서 가지고 있는 NFT를 보여주는 기능을 제공한다.

네트워크 별 잔고 및 Nonce, 가스비 표시

블록체인에는 NonceGas fee라는 개념이 존재한다. Nonce는 일종의 counter같은 것인데 한 개의 계정에서 transaction을 날린 횟수만큼 이 counter의 값이 하나씩 증가하게 된다. 동일한 transaction을 시간차를 두고 보내더라도 이 counter의 값을 통해 각각을 차별화 할 수 있다.

Gas fee는 블록체인에서 기존 체인에 어떻게 블록이 붙고 이를 어떻게 합의하는 과정을 알아야 전반적인 이해가 가능하다. 블록체인에는 크게 2개의 주체가 있는데, 하나는 WEB3 생태계에서 활동하면서 transaction을 생성하는 파티이고 다른 하나는 앞선 파티들이 만든 transaction이 맞는지 validate, nonce(앞서 설명한 nonce와는 다른 개념)를 이리저리 바꾸어 가면서 해시 함수에 넣었을때 특정 길이의 leading zero를 만드는 Nonce를 구하는 파티가 존재한다.

여기서 이 2번째 파티를 Miner이라고 하는데 Miner들은 transaction pool에 존재하는 transaction들 중 몇 개를 골라 block을 만들 수 있다(이로 인해 MEV와 같은 현상이 일어나기도 한다). 이때 Block에 담을 transaction들은 임의로 고르는 것이 아니라 Miner가 해당 block을 main-chain에 붙였을때 보상을 받게 되는 Gas Fee순으로 담게 된다. 따라서 Gas FeeMiner들에게 돌아가는 보상이고 트잭을 날리는 주체는 높은 Gas Fee를 설정해야지 블록에 담길 확률이 올라간다.

플러그인에서 제공하는 getTransactionCount, gasPrice와 getBalance

고맙게도 플러그인에서 Async 형태로 Transaction Count(Nonce), gasPricegetBalance를 가져오는 함수를 제공하고 있다. 앞서 설정한 Blockchain Config을 통해 해당 함수의 Input으로 넣는다. 헷갈릴 수 있는게 Nonce라는 용어는 Transaction Count를 표현하기도 하고 Leading Zero를 구하는 값을 의미하기도 한다. 여기서는 전자의 뜻으로 사용했다. Gas Price는 현재 네트워크의 상황을 알아보는 중요한 지표이며 Get Balance는 선택한 네트워크의 native token의 수량을 보여준다. NonceGas Price은 이따가 Signing Transaction을 할때 필요한 Input들이기도 하다.

지갑 상에서 네트워크 전환

Transaction Count(Nonce), gasPricegetBalance을 구했다면 이제는 UI를 디자인 해보자.

사용자가 네트워크를 ComboBox 선택창을 통해 고르면, 언리얼 UMG에서 제공하는 ComboBoxSelection Changed delegate를 통해 선택한 네트워크를 Blockchain Config로 설정한다. TxCount가스비, 잔고에 해당하는 Text에 각각 Transaction Count, Gas Price, Balance를 바인딩한다. 바인딩은 tick마다 값을 없데이트하기 때문에 Blockchain Config에 설정된 네트워크로 변환하자마다 delta second이후 바로 값이 업데이트 된다.


토큰 전송 기능

Signing & Sending Transaction

이제 마지막 관문인 토큰 전송기능이 남았다. 놀랍게도 토큰 전송은 관련 함수들만 알면 우리가 가지고 있는 값들을 통해 손쉽게 구현 가능하다.

Sign Transaction 함수와 sendRawTransaction 함수

먼저 Signing Transaction하는 과정이 있다. 여기에는 누가(보내는 사람의 Private Key) 누구에게(받는 사람의 Public Key) 토큰을 전송할지에 대한 정보가 입력된다. 여기에는 비대칭키(Asymmetric key) 암호화 알고리즘이 사용되어서 수신자와 송신자가 동일한 비밀키를 가지지 않아도 안전한 전송이 가능하다.

송신자의 Private Key수신자의 지갑 주소 이외에도 Nonce, Sending Address, Gas price, Gas limit와 같은 값들을 Input 값에 추가적으로 집어 넣은 다음에 sendRawTransaction을 통해 최종적으로 토큰 전송을 해보자.

주의하여야 할 점은 Signing TransactionsendRawTransaction을 할때는 Blockchain으로부터 얻어오는 값을 고정시켜야 한다. 앞서 설명을 못했지만 Wallet UI의 Event Counstrcut에서 Set Timer By Eventcustom eventdelegate로 연결시켜서 일정 시간마다 Blockchain의 값과 계정 정보를 업데이트 시켰다. 트잭을 보내는 순간에는 업데이트를 멈춰야 함으로 timer handle을 가져와 timer를 pause를 시키고 blockchain을 pending state로 잠시동안 만들어준다.


최종 화면

Sepolia Network를 통해 0.1ETH 전송

먼저, Google에서 제공하는 Sepolia Faucet으로 SaveGame에 저장되어 있는 주소에 테스트 토큰을 전송해 놓았다. 그 다음 저장된 주소로부터 내 개인 메타마스크 계정으로 Sepolia 네트워크 상에서 0.1ETH 만큼의 토큰을 보내보자.

보내는 주소와 보내는 수량에 각각 수신자의 지갑주소와 0.1ETH를 입력하고 보내기 버튼을 누르면, Transaction Count에 해당되는 TxCount가 5에서 6으로 변하고 잔고가 0.2ETH에서 0.1ETH로 감소하는 것을 화면상으로 확인할 수 있다!

Sepolia Etherscan으로 트잭 확인

아직도 믿지 못하겠다면(충분히 그럴만하다) 해당 계정의 전체 트잭 히스토리를 볼 수 있는 Sepolia Etherscan에서 또한 확인이 가능하다. 긴 주소를 클립보드에 복사하는 기능을 넣기 위해서 Low Entry라는 플러그인을 설치했다. 그림에서 빨간색 박스에 표시된 것처럼 성공적으로 트랜잭션이 들어왔다.


Reference

타원 곡선 알고리즘

3S Game Studio

profile
🏦KAIST EE | 🏦SNU AI(빅데이터 핀테크 전문가 과정) | 📙CryptoHipsters 저자

0개의 댓글