// 에드센스

오늘의 강의는 뭘까~용


1. 스마트 컨트랙 코드는 실행할때 돈이 든다. 

왜?

나의 트랜잭션을 블록에 넣어줄 마이너는 땅파서 컴퓨터를 돌려주는게 아니기때문.

 

얼마나 들까?

밑에서 대~강 구해봤다.

 

왜 알아야하나?

알아야 나중에 코딩할때 더 값싼 방법으로 코딩할 수 있으니까

 

가스비는 일반적으로 gwei로 표시되며, 이는 ETH의 10억분의 1(0.000000001 ETH)이다

 

이더리움의 수수료

수수료 = gas(gasLimit) * gasPrice

 

예시)

수수료 = gas(21000) * gasPrice(1gwei)

수수료 = 21000000000000gwei

이때, 1ETH = 10 ** 18wei이니까

 

수수료 = 0.00021ETH 가 된다.

 

gas와 gasPrice는 다음과 같은 차이점이 있다.

  • gas(gasLimit) : 본 송금 '작업'에서 소비되는 가스량
    (estimated 한 수치라서 변경가능하다. 하지만 넘 작게하면 거부됨)
  • gasPrice : 내가 가스당 지불할 가격
    (경매처럼 내가 금액을 제안하는 것임)

 

MAX TOTAL : gas * gasPrice 로 형성된 최종지불금액

gasPrice를 높게 잡을수록 이더리움 블록체인에서 마이너들이 내 트랜잭션을 빠르게 채굴해준다.

 

 

 

 

확인해보자

이더스캔에서 아무 트랜잭션이나 한번 봐보자

 

Transaction Fee(0.000739157072325) = gas(21000) * gasPrice(0.000000035197955825)

임을 확인할 수 있다.

 

Gas Limit & Usage by Txn은 뭐냐?

Gas Limit(앞에서 말한 gas와 동일)은 내가 이 값 이상으로는 가스비를 내지 않을 것이다라는 최대한도

Usage는 실제로 사용된 가스비를 의미

이 예시의 경우는 Limit의 100%를 사용했다.

 

넉넉하게 Gas Limit을 주면 알아서 최적의 가스비로 지불이 된다.

너무 GasLimit을 낮게주면 트랜잭션이 실패되니 넉넉하게 주자

 

 

 

 


2. 우리가 작성한 스마트컨트랙이 사용할 Gas를 대~강 계산해보자

가스비가 많이드는 연산: 블록체인의 용량을 늘리는 연산(저장하는 연산)

 

 

참고로 한 블록에서 사용가능한 가스비가 정해져있기에(Block Gas Limit) 무제한으로 용량을 늘리는(정보를 저장하는) 연산을 할 수 없다.

 

이 블록안에 있는 모든 트랜잭션 들이 사용할 수 있는 gas limit은 30,029,295이다.

이 값을 초과할 정도의 연산은 블록에 들어갈 수 없다.

 

이더리움의 가스비는 다음과 같이 정해져있다

  • 32Bytes 저장에 20000gas가 든다
  • 32Bytes 기존 변수에 있는 값을 바꿀때 5000gas
  • 기존변수를 초기화해서 더 이상 쓰지 않을때 10000gas return

 

 

Lottery.sol의 가스비를 파헤쳐보자

우리가 만든 Bet함수를 실행할 때 발생하는 가스비를 계산해보자

truffle console을 통해서 Bet함수를 실행시켜보았고 90846gas가 발생했다

 

 

연속으로 한번 더 실행하면 75846gas가 발생한다.

왜 갑자기 적어졌지? 끝에서 설명하겠다.

 

 

 

 

일단 우리가 작성한 코드를 보자

 

  function bet(bytes challenges) public payable returns (bool result) {
    // 돈이 제대로 왔는지 확인
    // require는 if역할, msg.value는 컨트랙트가 받은금액, 문자열은 조건이 false때 출력할 문구
    require(msg.value == BET_AMOUNT, 'Not Enough ETH');

    // 큐에 베팅정보를 넣기
    require(pushBet(challenges), 'Fail to add a new Bew Info');

    // 이벤트 로그를 찍는다
    // (몇번째 배팅인지, 누가 배팅했는지, 얼마 배팅했는지, 어떤글자로 베팅했는지, 어떤 블록에 정답이 있는지)
    emit BET(_tail - 1, msg.sender, msg.value, challenges, block.number + BET_BLOCK_INTERVAL);
    return true;
  }

 

이러한 Bet함수에서 pushBet을 호출하는 부분이벤트를 emit하는 부분에서 가스비가 든다.

즉, 첫번째 발생한 90846가스로 계산을 시작해보면

90846 = 기본가스(21000) + pushBet(???가스) + emit(대략 5000gas)

 

 

자 그러면 pushBet함수를 보자

  function pushBet(bytes challenges) internal returns (bool) {
    BetInfo memory b; 
    b.bettor = msg.sender; // address형 변수 사용 (1)
    b.answerBlockNumber = block.number + BET_BLOCK_INTERVAL; // uint256형 변수 사용 (2)
    b.challenges = challenges; // bytes형 변수 사용 (3)

    _bets[_tail] = b; // 그닥 크지 않다 (4)
    _tail++; // 32Byte형 변수 사용(5)

    return true;
  }

(1)번은 address형 변수를 사용했으므로 20Byte를 사용한 것

(3)번은 Byte형 변수를 사용했으므로 1Byte를 사용한 것

이더리움 블록체인에서 사용하는 기본저장단위는 32Byte다

 

즉, 20Byte를 사용했든 1Byte를 사용했든 둘 다 32Byte를 사용한 것이므로 64Byte를 사용한 것이 맞으나

작은 단위의 바이트에 대해서는 쪼개서(?) 연산해준다고 한다.

따라서 (1)번과 (3)번 합쳐서 32Byte를 사용한 셈 치면 된다.

(1) + (3) = 20000gas (32Byte)

 

(2)번은 uint256형 변수를 사용했으므로 32Byte 사용한 것

(2) = 20000gas (32Byte)

(4) = 무시

(5) = 20000gas (32Byte)

 

 

즉, pushBet에서 발생한 gas는 (1) + (2) + (3) + (4) + (5) = 60000gas

 

 

90846 = 기본가스(21000) + pushBet(60000gas가스) + emit(대략 5000gas)

90846 = 86000gas + 기타 자잘한 연산들

 

이라고 할 수 있다.

 

그러면 왜 2번째 실행했을때는 75846gas만 발생했느냐?

 

32Bytes 기존 변수에 있는 값을 바꿀때 5000gas가 들기때문이다.

  function pushBet(bytes challenges) internal returns (bool) {
    BetInfo memory b; 
    b.bettor = msg.sender; // 함수 호출한 사람
    b.answerBlockNumber = block.number + BET_BLOCK_INTERVAL; 
    b.challenges = challenges; 

    _bets[_tail] = b;
    _tail++; // 초기 0에서 1되는 것은 20000gas지만 1에서 2되는 것은 수정이므로 5000gas만 든다

    return true;
  }

즉 첫번째 실행보다 15000gas가 덜 발생한다.

90846 - 15000 = 75846

대강 맞는것을 확인할 수 있다.

 

므ㅏ

강의출처:


1. 사용할 컨트랙에 대한 정보를 가져온다.

contract("Lottery", ([deployer, user1, user2]) => {
  ...
});

트러플에서는 mocha와 chai를 스마트컨트랙에서 사용할 수 있도록 조절해 놓았다고 한다.

따라서 테스트를 시작할 때 contract 키워드로 시작하면 된다.

 

첫번째 인자로는 컨트랙트 이름이 들어가고,

두번째 인자로는 콜백함수가 들어간다. 이 콜백함수의 파라미터로 ganache-cli에서 생성되는 10개의 account가 들어간다.

즉 최대 10개의 인자를 넣을 수 있다.

 

위 코드처럼 3개를 썼다는 것은 

deployer에 0xd53....

user1에 0xd07....

user2에 0x040....

이렇게 3개의 주소가 파라미터로 들어간 것이다.

 

 

 

 


2. 스마트 컨트랙 배포

테스트를 하려면 스마트컨트랙 코드가 블록에 들어가서 실행되어야 하므로 컨트랙을 배포해주자

contract("Lottery", ([deployer, user1, user2]) => {
  beforeEach(async () => {
    console.log("Before each");
    lottery = await Lottery.new(); //테스트에서 사용할 스마트 컨트랙 배포
  });
});

 

 

 

 


3. 테스트코드 작성

  let betAmount = 5 * 10 ** 15;
  let BET_BLOCK_INTERVAL = 3;
  
  describe("Bet", async () => {
    it("베팅큐에 값이 잘 들어갔는지 확인하기", async () => {
      // 베팅한다
      const receipt = await lottery.bet("0xab", {
        from: user1,
        value: betAmount, // 5 * 10 ** 15 -> 0.005ETH
      });

      // pot머니(상금)은 아직 결과가 들어나지 않았기에 0이어야 한다.
      let pot = await lottery.getPot();
      assert.equal(pot, 0);

      // 컨트랙트 주소로 0.005이더가 들어왔는지 확인한다
      let contractBalance = await web3.eth.getBalance(lottery.address); // web3가 자동으로 주입돼있다
      assert.equal(contractBalance, betAmount);

      // 베팅인포가 제대로 들어갔는지 확인한다
      let currentBlockNumber = await web3.eth.getBlockNumber();
      let bet = await lottery.getBetInfo(0); // 큐 제일 앞의 베팅정보를 가져온다

      // 큐에 넣은 베팅정보가 올바른지 확인
      assert.equal(
        bet.answerBlockNumber,
        currentBlockNumber + BET_BLOCK_INTERVAL
      );
      assert.equal(bet.bettor, user1);
      assert.equal(bet.challenges, "0xab");

      // 로그(BET이라는 이벤트)가 제대로 찍혔는지 확인한다
      await expectEvent.inLogs(receipt.logs, "BET");
    });

    it("0.005이더가 안들어왔을때는 실패해야한다", async () => {
      // 트랜잭션 실패
      await assertRevert(
        // 두번째 인자는 트랜잭션 오브젝트, transaction object란
        // (chainId, value, to, from, gas(limit), gasPrice)
        lottery.bet("0xab", { from: user1, value: 4000000000000000 }) // bet함수 실행 -> 0.004ETH가 들어갔기에 트잭이 실패나야한다
      );
    });
  });

 

 

event, emit을 테스트하기

const assert = require("chai").assert;

// 직접 발생시킨 event의 로그가 있는지 확인한다.
const inLogs = async (logs, eventName) => {
  const event = logs.find((e) => e.event === eventName);
  assert.exists(event);
};

module.exports = {
  inLogs,
};

[Lottery Dapp 개발하기] 2.4까지의 분량이다. 

밀린 블로그 글써내기중이라 그간 했던 분량을 한번에 쑤셔넣는중.

이 글을 읽는 여러분들에게 도움이 되면 좋겠지만 딱히 그럴려고 쓰는 글은 아니고 혼자서 끄적이는 글.

따라서 생략된 설명이나 이걸 왜 설명? 하는 부분이 있을것이다.

 

 

가보자~

강의 출처:


1. 코딩할 것들

  • 스마트 컨트랙
    • 베팅정보를 담는 구조체
    • 베팅정보를 저장할 큐 자료구조
    • 쌓인상금_가져오기()
    • 베팅큐에서_베팅정보_가져오기()
    • 베팅하기()
      • 베팅을_베팅큐로_밀어넣기()
      • 베팅을_베팅큐에서_빼기()
  • 테스트코드 (다음글에서)
    • beforeEach
    • 베팅큐에 베팅정보가 잘 들어간경우 테스트
    • 베팅큐에 베팅정보가 못들어간경우 테스트

 

 

 


2. Lottery.sol에 코딩한 것들

베팅 정보를 담는 구조체

  struct BetInfo {
    uint256 answerBlockNumber; // 맞출 블록의 넘버
    address payable bettor; // 돈을 건 사람 주소, 특정주소에 돈을 보내려면 payable을 써줘야함
    bytes challenges; // 문제. ex) 0xab
  }

 

 

 

BetInfo 구조체를 넣어줄 큐를 구현

  // 매핑으로 큐를 구현하기 위한 변수
  uint256 private _tail;
  uint256 private _head;

  // 키는 uint, 값은 BerInfo인 매핑
  mapping(uint256 => BetInfo) private _bets;

index - value의 쌍을 mapping을 통해 구현한다.

 

 

 

상금 액수 조회하기

  uint256 private _pot;
  
  // 스마트 컨트랙의 변수를 가져와서 쓰려면 view 키워드를 쓴다
  // 더 자세히는, Storage 자료를 읽어서 보여줄때 사용. 수정할때는 사용x
  // Storage 자료는 블록에 영구적으로 기록된 값(하드디스크)
  // 반대인 Memory 자료는 임시로 저장되는 값(RAM)
  function getPot() public view returns (uint256 value) {
    return _pot;
  }

 

 

 

베팅하기

  // 이벤트 객체
  event BET(uint256 index, address bettor, uint256 amount, bytes1 challenges, uint256 answerBlockNumber);

  function bet(bytes challenges) public payable returns (bool result) {
    // 베팅할 금액(0.005ETH)이 제대로 왔는지 확인
    // require는 if역할, msg.value는 컨트랙트가 받은금액, 'Not Enough ETH'은 조건이 false때 출력할 문구
    require(msg.value == BET_AMOUNT, 'Not Enough ETH');

    // 큐에 베팅정보를 넣기
    require(pushBet(challenges), 'Fail to add a new Bew Info');

    // 이벤트 객체를 체인에 기록한다
    // (몇번째 배팅인지, 누가 배팅했는지, 얼마 배팅했는지, 어떤글자로 베팅했는지, 어떤 블록에 정답이 있는지)
    emit BET(_tail - 1, msg.sender, msg.value, challenges, block.number + BET_BLOCK_INTERVAL);
    return true;
  }

event와 emit

트랜잭션이 완료되면 트랜잭션 영수증을 발행한다.

영수증에는 트랜잭션 실행 동안 발생했던 모든 행동로그가 기록되어 있다.

event는 이런 로그를 만들기 위한 객체

emit은 만들어진 event 객체를 체인에 올리기.

더보기

 

 

 

 

베팅정보들을 담은 큐에서 베팅정보 1개 꺼내오기

  function getBetInfo(uint256 index)
    public
    view
    returns (
      uint256 answerBlockNumber, // 반환값1
      address bettor, // 반환값2
      bytes challenges // 반환값3
    )
  {
    BetInfo memory b = _bets[index]; // memory형 변수는 함수가 끝나면 지워짐, storage형 변수는 블록에 영영 기록됨
    answerBlockNumber = b.answerBlockNumber; // 반환값1
    bettor = b.bettor; // 반환값2
    challenges = b.challenges; // 반환값3
  }

 

 

 

베팅큐에 베팅정보 push

  function pushBet(bytes challenges) internal returns (bool) {
    BetInfo memory b; // 베팅정보를 하나 생성하고 세팅한다
    b.bettor = msg.sender; // 함수 호출한 사람
    b.answerBlockNumber = block.number + BET_BLOCK_INTERVAL; // block.number는 현재 이 트랜잭션이 들어가게되는 블록넘버를 가져온다
    b.challenges = challenges; // 내가 베팅한 값

    _bets[_tail] = b; // 큐에 넣고
    _tail++; // 테일 포인터 조정

    return true;
  }

 

 

 

베팅큐에서 베팅정보 1개 pop

  function popBet(uint256 index) internal returns (bool) {
    // 스마트컨트랙에서 덧셈이든, 뺄셈이든 하면 가스를 소모한다.
    // delete를 하면 가스를 돌려받는다. 왜? 이더리움 블록체인에 저장하고 있는 데이터를 더 이상 저장하지 않겠다는 것이기에
    // 즉, 상태 데이터베이스에 있는 값을 그냥 가져오겠다는 것이기에
    // 그러니 필요하지 않은 값이 있다면 delete를 해주자
    delete _bets[index];
    return true;
  }

 

 

 

 


3. 전체 코드

pragma solidity >=0.4.21 <0.6.0;

contract Lottery {
  struct BetInfo {
    uint256 answerBlockNumber; // 맞출 블록의 넘버
    address payable bettor; // 돈을 건 사람 주소, 특정주소에 돈을 보내려면 payable을 써줘야함
    bytes challenges; // 문제. ex) 0xab
  }

  // 매핑으로 큐를 구현하기 위한 변수
  uint256 private _tail;
  uint256 private _head;

  // 키는 uint, 값은 BerInfo인 매핑
  mapping(uint256 => BetInfo) private _bets;

  address public owner;

  uint256 internal constant BLOCK_LIMIT = 256; // 블록해시를 확인할 수 있는 제한
  uint256 internal constant BET_BLOCK_INTERVAL = 3; // 2번 블록에서 베팅을 하면 5번 블록에서 결과가 나온다
  uint256 internal constant BET_AMOUNT = 5 * 10**15; // 0.005ETH

  uint256 private _pot;

  // 이벤트 로그들을 한번에 모을 수 있다, BET이라는 로그들을 찍어줘
  // (몇번째 배팅인지, 누가 배팅했는지, 얼마 배팅했는지, 어떤글자로 베팅했는지, 어떤 블록에 정답이 있는지)
  event BET(uint256 index, address bettor, uint256 amount, bytes1 challenges, uint256 answerBlockNumber);

  constructor() public {
    owner = msg.sender; // msg.sender는 전역변수
  }

  function getPot() public view returns (uint256 value) {
    // 스마트 컨트랙의 변수를 가져와서 쓰려면 view 키ㅕ드를 쓴다
    return _pot;
  }

  // Bet (베팅하기)
  /*
    @dev 베팅을 한다. 유저는 0.005ETH를 보내야하고 베팅용 1byte 글자를 보낸다, 큐에 저장된 베팅 정보는 이후 distribute 함수에서 해결한다
    @param challenges 유저가 베팅하는 글자
    @return 함수가 잘 수행되었는지 확인하는 bool 값
    */
  function bet(bytes challenges) public payable returns (bool result) {
    // 돈이 제대로 왔는지 확인
    // require는 if역할, msg.value는 컨트랙트가 받은금액, 문자열은 조건이 false때 출력할 문구
    require(msg.value == BET_AMOUNT, 'Not Enough ETH');

    // 큐에 베팅정보를 넣기
    require(pushBet(challenges), 'Fail to add a new Bew Info');

    // 이벤트 로그를 찍는다
    // (몇번째 배팅인지, 누가 배팅했는지, 얼마 배팅했는지, 어떤글자로 베팅했는지, 어떤 블록에 정답이 있는지)
    emit BET(_tail - 1, msg.sender, msg.value, challenges, block.number + BET_BLOCK_INTERVAL);
    return true;
  }

  // 베팅한 값을 큐에 저장함

  // Distribute (검증하기)
  // 베팅한 값을 토대로 결과값을 검증
  // 검증결과가 틀리면 팟머니에 돈을 넣고, 맞으면 돈을 유저에게 준다

  // 베팅정보들을 담고있는 큐에서 베팅정보 가져오기
  function getBetInfo(uint256 index)
    public
    view
    returns (
      uint256 answerBlockNumber,
      address bettor,
      bytes challenges
    )
  {
    BetInfo memory b = _bets[index]; // memory형 변수는 함수가 끝나면 지워짐, storage형 변수는 블록에 영영 기록됨
    answerBlockNumber = b.answerBlockNumber; // 반환값1
    bettor = b.bettor; // 반환값2
    challenges = b.challenges; // 반환값3
  }

  // 큐 push
  function pushBet(bytes challenges) internal returns (bool) {
    BetInfo memory b; // 베팅정보를 하나 생성하고 세팅한다
    b.bettor = msg.sender; // 함수 호출한 사람
    b.answerBlockNumber = block.number + BET_BLOCK_INTERVAL; // block.number는 현재 이 트랜잭션이 들어가게되는 블록넘버를 가져온다
    b.challenges = challenges; // 내가 베팅한 값

    _bets[_tail] = b; // 큐에 넣고
    _tail++; // 테일 포인터 조정

    return true;
  }

  // 큐 pop
  function popBet(uint256 index) internal returns (bool) {
    // delete를 하면 가스를 돌려받는다. 왜? 상태데이터베이스에 저장된 값을 그냥 뽑아오겠다는 것이기에
    // 그러니 필요하지 않은 값이 있다면 delete를 해주자
    delete _bets[index];
    return true;
  }
}

정말 오~랜만에 글을 쓴다.

써야지 써야지 하면서 너무 미뤘다.

 

 

 

더 이상 글을 안쓰면 블로그를 영영 잊게될까봐 다시 자리에 앉았다.

그래도 그간 쪼잡쪼잡 이거저거 맛보기는 하고있었는데 그 중 하나인 이더리움 dapp 개발 과정을 써볼까 한다.

유튜브 강좌를 보며 따라하는 것이고.. 그저 따라할 뿐...! 아직 어렵다

 

코딩하는 나의 모습

 

내가 보며 따라하는 강좌는 요거다.

https://youtu.be/Ud3_OrxNPDg

 

 


0. What we gonna do?

스마트컨트랙트를 기반으로하는 디앱을 만들어보는 프로젝트.

디앱이란?

대충 블록체인을 활용한 애플리케이션.

Decentralized App

 

 

 


1. Lottery Dapp preview

로또게임(?)을 만들건데 스마트컨트랙을 활용하여 만들 것이다.

 

이 프로젝트에서 우리는

  • 솔리디티를 사용한 스마트컨트랙 만들기
  • 배포스크립트 만들기
  • 테스트코드 만들기
  • 프론트에 연동하기

를 하게된다.

 

 

스마트컨트랙을 작성하여 블록체인에 올린다는 것은,

자바로 백엔드 코드를 작성하여 EC2에 올린다는 것과 같다.

 

블록체인도 결국 분산된 무수히 많은 컴퓨터들이니 블록이 곧 서버역할을 하는 셈이다. 스마트컨트랙은 코드역할이고.

따라서 기존 애플리케이션의 흔한 구조인

[프론트엔드]-[백엔드]가

[프론트엔드]-[블록체인]이 되는 것이다.

 

 

단, 경우에 따라서 디앱이라도 [프론트엔드]-[백엔드]-[블록체인]처럼 백엔드를 추가하는 구조도 많은데 아무래도 블록체인상에서 코드를 실행시키기 위해서는 가스비라는 비용이 발생하기에 이런 비용적인 측면을 개선하고자 백엔드단을 추가하기도 한다고 한다.

(블록체인을 구성하는 노드(컴퓨터)들을 제공하는 사람들은 땅파서 장사하는게 아니기에.. 이들에게 가스비라는 대가를 지불하고 우리의 코드를 실행시켜달라고 하는 것)

 

 

 

 

아무튼, 서론이 길었다.

 

우리가 만들 로터리 디앱은 어떤 프로젝트냐면,

https://youtu.be/div91ADl_7k

시연영상

 

 

블록해시 맞추기 게임이다.

 

내가 만든 트랜잭션이 들어가게 되는 블록보다 3개 뒤 블록의 블록해쉬를 때려맞추는 게임(재미는 없을듯)

 

 

예를들자면

  1. 내가 0xab라고 베팅을 한다.
  2. 이 베팅은 하나의 트랜잭션으로서 블록에 포함된다.
  3. 만약 이 트랜잭션이 3번 블록에 포함됐다고 하자.
  4. 3번 블록의 3개 뒷 블록인 6번 블록의 블록해쉬값이 0xab라면 내가 이긴 것이다.
  5. 즉 6번 블록이 생성완료되고 7번블록의 생성이 시작된 시점부터 게임결과를 알 수 있다.

 

pot머니라고 부르는 베팅금액을 가져가는 규칙은 이러하다.(전부 0.005ETH씩 베팅한다)

  1. 6번 블록이 완성되었을때 유저들이 베팅한 돈을 팟머니에 쌓는다.
  2. 맞춘사람이 여러명일 경우 첫번째로 맞춘 사람이 가져간다.
  3. 두글자중(ab중에서) 하나만 맞추었다면 돈을 돌려준다.
  4. 결과값을 검증할 수 없는 경우가 발생하는 경우 돈을 돌려준다.

 

 

동작으로만 보면 그닥 복잡하진 않아보인다.

이 프로젝트의 핵심은 스마트컨트랙을 작성하고 이를 배포하여 체인과 상호작용하는 것에 있다.

 

 

 

 


2. 개발 환경

  • NodeJS : 배포와 테스트 코드를 작성하기 위해
  • VSCode : ide 이거씀
  • Truffle : 솔리디티를 컴파일하고 스마트컨트랙을 테스트, 배포할 수 있는 블록체인 개발 프레임워크이다. Truffle 설치시 solc-js가 설치된다(JS기반의 솔리디티 컴파일러)
  • ganache-cli : 간이 블록체인. 근데 이제 내 PC 메모리로 실행되는. 블록체인을 쉽게 mock을 해놓은 툴(테스트 용도), 즉 테스트와 개발에 사용하는 이더리움 RPC 클라이언트.(테스트용 노드(서버)의 느낌으로 이해하면 되려나..?)

 

ganache-cli(편의상 가나슈라고 하겠다)를 실행하면 로컬에서 블록체인 환경이 실행되고 여기서 사용할 수 있는 계정 10개가 제공된다. 

 

이 간이 블록체인을 켜기 위해서는 콘솔창에서

ganache-cli -d -m tutorial

를 실행해준다

 

-d 옵션을 주면 생성되는 니모니깅 동일하고 주소와 키값이 동일하다

-m 옵션은 지정된 HD 지갑 니모닉 값으로 초기 주소를 생성하기 위해 사용한다.

우리는 니모닉으로 tutorial을 사용했다.

보통 니모닉은 12개 단어를 사용하지만 1개만 써도 된다(?)

 

즉 -d, -m 옵션을 사용하고 니모닉 단어(tutorial)를 사용하면 항상 같은 지갑주소가 생성된다.

참 오랜만에 글을 쓴다.... 분발하자


1. CEX란?

  • Centralized Exchange로 "중앙화거래소"를 의미한다
  • 우리가 많이 사용하는 업비트, 빗썸, 코인원과 같은 거래소

 

 

CEX의 장점

  • 암호화폐에 대해 잘 몰라도 쉽게 접근할 수있는 낮은 진입장벽
  • 거래 처리속도가 빠르다
  • 개인키를 거래소가 알아서 관리해준다
  • 거래량이 많다

 

 

CEX의 단점

  • 중앙 서버가 공격 당아혐 우리의 자산이 위험하다
  • 가두리 펌핑

 

 

 

CEX(중앙화 거래소)에서 코인을 사면 어떤 일이 벌어질까? 실제로 트랜잭션이 발생할까?

코인을 나의 소유로 만든다는 것은 블록체인 네트워크 상에서 나의 거래 트랜잭션이 기록되고 이것이 기록된 블록이 생성됨을 의미한다.

나는 그간 아무생각없이 업비트에서 코인을 사고 팔아만 왔다. 그럼 코인을 사고 팔때마다 네트워크상에 나의 거래 기록이 기록될까?

 

결과는 "아니다"

조금이라도 생각해보면 당연한 결과지만 그간 아무 생각없이 침팬치마냥 매수/매도 버튼만 눌러왔기에 신선한 충격을 받았다.

코인별로 블록이 생성되는 시간도 다 다르고 소모되는 비용도 천차만별이기에 업비트에서 일어나는 모든 거래를 트랜잭션으로 기록하는 것은 너무 낭비이며 비효율이다.

 

22년 1월 27일의 도지코인

하지만 이런 중앙화 거래소에서도 실제 트랜잭션이 일어나는 순간은 있다.

바로 외부 지갑으로(에서) 이동할 때 실제 트랜잭션이 발생한다.

 

중앙거래소에서의 코인 거래는 "원화 포인트와 가상자산 포인트간의 매매 or 교환 행위"로 이해하는 것이 일반적이라고 한다.

 

 

 

업비트에서 비트코인을 사고 외부 거래소 지갑(이를테면 바이낸스)로 옮기는 작업을 예시로 들어보겠다

1. 우리가 업비트에 8천만원을 입금한다. 8천만원은 업비트의 운영 법인으로 이전되고 우리는 업비트에 8천만원어치의 반환채권을 가지게 된 셈이다. 간단하게 8천만 포인트라고 해도 될 것 같다. 우리는 업비트에서 이 8천만 포인트로 비트코인 10개를 구입한다.

 

 

2. 우리가 8천만 포인트로 10비트코인을 구입했다는 기록은 업비트의 중앙서버에 기록된다.

우측의 거래소 BTC지갑은 업비트가 가지고있는 실제 비트코인이 있는 지갑이다. 이 지갑의 비트코인 수량에는 아무런 변화가 없음에 주목하라.

 

 

 

3. 우리는 이제 이 10비트코인을 나의 바이낸스 지갑으로 옮기고 싶어한다. 그러면 업비트의 비트코인 공용 지갑에서 업비트 개인 비트코인 지갑으로 전송이 일어나며 이 이동은 트랜잭션으로 기록된다.(트랜잭션 1회)

 

 

 

4. 이어서 업비트 개인 비트코인 지갑에서 외부 주소로 이동이 발생하고 이 또한 트랜잭션이 발생한다.(트랜잭션 2회)

그림은 하드웨어 개인 BTC 지갑이라고 나와있지만 아무튼 거래소 외부로의 이동임이 핵심이다.

 

 

이렇게 그림으로 살펴봤을때는 실제 트랜잭션이 총 2번 발생했다.

정말 그럴까?

마침 바이낸스 -> 업비트로 트론을 전송할 일이 있었기에 트론스캔을 통해 트랜잭션을 확인해 보았다.

 

 

1. 먼저 나의 업비트 트론지갑 주소는 다음과 같다. TTU~~~이다

 

 

2. 트론스캔을 통해 확인해보면 TAz -> TTU로 트랜잭션이 발생했음을 확인할 수 있다.

TTU는 나의 업비트 트론 지갑 주소이면 TAz는 대충 바이낸스쪽 나의 트론 지갑 주소임을 알 수 있다.

TTU -> TRQ로의 이동도 확인할 수 있는데 대충 나의 트론 개인지갑 -> 업비트 트론 공통지갑으로의 이동임을 추측해볼 수 있다.

 

바이낸스 -> 업비트 개인지갑으로 이동이 일어나고 2시간 뒤에 업비트 개인지갑 -> 업비트 공용지갑으로 트랜잭션이 일어남을 확인할 수 있다. 업비트측에서 유저 개인지갑 -> 공용지갑으로의 이동은 외부지갑에서의 이동과 상관 없이 발생하는가보다(뇌피셜)

 

 

 

 


2. DEX

  • Decentralized Exchange, 즉 탈중앙화 거래소다
  • 업비트, 빗썸처럼 중개자가 없다
  • 모든것은 스마트 컨트랙을 통해 P2P로 진행된다
  • 중앙 거래소의 기능을 스마트 컨트랙으로 대체한 것
  • 모든 거래가 자동화 되어있고 모든 거래는 블록체인 상에서 진행된다
  • 유니스왑, 스시스왑, 팬케이크 스왑 등이 있다

 

 

DEX 장점

  • 보안적으로 CEX보다 낫다.
  • 개인정보가 요구되지 않는다
  • 중앙거래소에 상장되지 않은 다양한 비상장 코인들이 있다

 

 

DEX 단점

  • 유동성이 필요하다
  • 실시간 거래 반영이 늦다
  • 고객센터가 없다 -> 모든 일을 내가 책임져야 한다
  • 중앙거래소보다 진입장벽이 높다

 

 

 

CEX, DEX 한 줄 비교

  • 집을 팔 때
  • CEX는 집을 공인중개사에게 완전히 넘기고 공인중개사가 파는 것
  • DEX는 공인중개사가 집을 살 사람만 찾아주고 내가 파는 것

CEX
DEX

 

 

 

 

 

 


AMM

  • DEX는 CEX처럼 오더북 기반으로 거래가 체결되지 않는다
  • AMM 프로그램 사용

AMM이란, 매수자와 매도자간의 거래를 이어주는 것을 탈중앙화 + 자동화 한 프로그램

 

 

AMM은 4가지 요소로 구성된다

  • 토큰 페어
  • 트레이더
  • 가격 결정 알고리즘
  • 유동성 공급자

 

 

유동성 공급자

  • 두가지 토큰에 대한 유동성을 풀에 공급하는 주체
  • 특정 토큰의 유동성이 없다면 거래를 할 수 없다(가격이 천정부지로 상승한다)

 

유동성 공급자에 대한 간단한 비유

-메-

우리가 흔히 아는 RPG게임에서 상점 시스템 상점주인으로부터 아이템을 사거나, 팔거나가 가능한 형태이다.

하지만 스카이림이라는 게임에서는 조금 다르다고 한다(이 게임을 해보진 않았다)

 

 

특이하게 상점주인이 가진 골드의 수량이 표시된다

메이플스토리에서는 우리가 아이템을 얼마나 많이 팔던 상점주인은 전부 사준다. 즉 상점 주인의 골드가 무한대였지만

스카이림은 상점주인이 가지고있는 골드가 제한되어 있기에 우리가 너무 비싼 아이템을 팔 수 없다.

 

 

이때 상점주인이 유동성 공급자의 역할을 한다고 볼 수 있다

  • 상점 주인은 아이템-골드 간의 거래 기회를 준다(유동성 공급자는 A토큰-B토큰간의 거래 기회를 준다)
  • 상인의 골드가 없다면 아이템을 팔 수 없다

 

 

 

가격결정 알고리즘

의 한 예시로 CPMM(Constant Product Market Maker)를 살펴보자

 

AMM 가격 결정 알고리즘의 한 종류

두 토큰 수량의 곱이 일정하게 유지되도록 가격을 경정하는 알고리즘

x와 y는 풀에 공급된 토큰의 수량이다

이더리움-오미세고 풀을 예시로 들어보자

 

 

풀에 10이더리움과 500오미세고가 들어있다(Exchange State1)

x * y = 5000이다

단순하게 생각했을때 1이더리움은 50오미세고의 가치를 가지는 것으로 보이기에 트레이더가 1이더리움을 풀에 넣는다면 50오미세고를 받게 될 것으로 예상된다

 

하지만 1이더리움을 넣는 순간 풀에 11개의 이더리움이 존재하므로 x * y = 5000이 유지되려면 오미세고는 454.5454...개가 되어야 한다. 따라서 트레이더는 500 - 454.5개의 오미세고를 받게된다. (약 45개) 

예상한 수량보다 적게 받은 이 상황을 슬리피지라고 부른다

 

 

 

 

참고:

더보기

https://www.coindeskkorea.com/news/articleView.html?idxno=70117

https://steemit.com/coinkorea/@bbkang/vs

 

중앙화거래소 vs 탈중앙화거래소 — Steemit

<중앙화 거래소의 문제점> 1.거래가 거래소 서버의 데이터베이스에서 이루어짐 2.거래소가 모든 기록과 거래(입출금, 매매)를 통제 3.개인지갑의 돈=거래소의 돈 (소유와 거래의 분리??) 현재 암호

steemit.com

https://bitkr.com/defi-kr/decentralized-exchanges/

 

탈중앙화 거래소(DEX)란 무엇이며 어떻게 운영되나요? | BitKR

탈중앙화 거래소는 DeFi 공간에서 '차세대 대물'로 선전되고 있습니다. 여기에서 작동 방식에 대한 모든 걸 읽어보세요.

bitkr.com

https://www.hankyung.com/economy/article/202110069443i

 

[김태린] 탈중앙화 거래소 떡상시대, DEX가 미래인가?

[출처: 셔터스톡] [스존의 존생각] 직장인 코인 투자자들에게는 눈을 붙이기도 힘든 나날의 연속이다. 재택근무가 조금씩 해제되기 시작했다. 연말정산 철도 다가왔다. ‘현금 빼고 다 간다’는

www.joongang.co.kr

https://medium.com/grabityorg/%EC%95%94%ED%98%B8%ED%99%94%ED%8F%90%EC%A7%80%EA%B0%91-%EA%B0%9C%EC%9D%B8%EC%A7%80%EA%B0%91-vs-%EA%B1%B0%EB%9E%98%EC%86%8C%EC%A7%80%EA%B0%91-feat-%ED%94%8C%EB%9E%98%EB%8B%9B%EC%9B%94%EB%A0%9B-b7ddaf394815

 

[암호화폐지갑] 개인지갑 VS 거래소지갑 (feat. 플래닛월렛)

GBT Protocol 커뮤니티 여러분 안녕하

medium.com

https://steemit.com/blockchain/@sidonyia/qnp3p

 

[암호화화폐] 거래소에서의 코인 거래 원리 (장기 미접속 청산주의) — Steemit

거래소에서 코인을 원화로 구입하여 거래할때의 원리를 알아보고자 합니다. 우리가 거래소에서 코인을 구매할때 실제 코인을 구매하는것이 아닌 거래소에서 만든 자체 정보로만 거래를 하고

steemit.com

https://academy.binance.com/ko/articles/what-is-an-automated-market-maker-amm

 

자동화된 시장 메이커(AMM)란 무엇인가요? | Binance Academy

자동화된 시장 메이커는 탈중앙 금융(DeFi) 내의 스마트 콘트랙트이며, 누구나 유동성 풀에 암호화폐를 예치하고 시장을 형성할 수 있게 합니다.

academy.binance.com

https://medium.com/@aiden.p/uniswap-series-1-%EC%9C%A0%EB%8B%88%EC%8A%A4%EC%99%91-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-e321446623c7

 

[UNISWAP SERIES] 1. 유니스왑 이해하기

유니스왑과 AMM을 쉽게 이해해보자. Reviewed by 정현, 오영택

medium.com

 

블록체인에 대해 전반적인 그림을 그리기 위해 조사하며 얻은 얕은 지식입니다.


0. 코인말고 블록체인

암호화폐에 대한 글은 아니고 카카오의 블록체인 프로젝트 클레이튼으로써 클레이튼 네트워크 전반에 대해 얕게 조사해 본것을 정리하고자 한다.

이번 포스팅은 클레이튼 공식 문서에 있는 소개 동영상을 보고 정리한 내용이다.

https://ko.docs.klaytn.com/bapp/tutorials/bapp-on-baobab-video-lecture/1.-introduction

 

1. 소개 - Klaytn Docs KO

Baobab에서 BApp 개발하기(동영상 강의)

ko.docs.klaytn.com

 

 

 

 


1. 다른 블록체인 플랫폼들의 약점

  • Scalability(확장성)
  • Finality(확정성)
  • Fork(분기발생)

 

 

 

1. Scalability(확장성)

"얼마나 많은 일을 신속하게 처리할 수 있는지"

 

를 의미한다.

이때 TPS와 Block Interval의 개념이 나온다.

TPS란 Transaction Per Second로, 초당 몇개의 트랜잭션(거래)인지를 의미하고.

Block Interval은 블록 생성 주기를 의미한다.

 

  • 비트코인은 보통 7 TPS
  • 이더리움은 보통 15~20 TPS

반면,

  • VISA카드는 1700 TPS를 가진다.

 

또 Block Interval을 보면

  • 비트코인은 10분
  • 이더리움은 15~20초다.

 

다시말해

이더리움이 20 TPS에 15초의 인터벌을 가진다면 20 * 15 = 300 transactions라는 것을 알 수 있다.

이더리움으로 우리가 송금을 한다면, 이더리움 블록이 생성이 되어야 거래가 성사되기에 15초의 대기시간이 필요하다. 물론 비트코인의 10분에 비해서 이더리움의 15초는 많이 개선된 것이긴 하지만 여전히 너무 느리다.

 

왜 이렇게 느리지?

기존 평범한 중앙 서버 방식에서는 큰 request가 들어올 경우 많은 서버들이 request를 나누어 병렬적으로 처리를 하기에 서버의 개수를 증가시키면 처리 속도를 늘릴 수 있었다.

하지만 블록체인 네트워크에서는 노드의 수가 아무리 많다해도 일을 분산해서 처리하는 것이 아닌 모든 노드가 모든 일을 한다. 즉, 전체 네트워크에서 가장 낮은 퍼포먼스의 노드를 기준으로 전체 네트워크의 속도가 낮아지는 하향평준화가 발생한다.

 

하지만,

킹레이튼은 3000 TPS와 Block Interval 1초를 가진다.

 

 

 

2. Finality(확정성)

FInal이란 트랜잭션이 블록에 기록되고 그 블록을 더 이상 수정할 수 없는 상태를 의미한다.

Finality는 블록이 더 이상 변경될 수 없는 상태까지 소요되는 시간을 의미한다고 할 수 있다.

 

이전 포스팅에서 51%공격과 컨펌의 개념에 대해 살짝 다뤘는데 그 내용인 듯 하다.

  • 비트코인은 블록생성(채굴)까지 10분이 걸리고, 60분의 Finality 시간이 걸린다.(10분씩 6번의 검증)
  • 이더리움은 블록생성(채굴)까지 15초갸 걸리고, 6분의 Finality 시간이 걸린다.(15초씩 25번의 검증)

 

즉, 내가 만약 비트코인으로 송금을 했고, 10분이 지나서 나의 거래기록이 저장된 블록이 생성되어 체인에 붙었다고 해도,

그 거래를 확정하려면 그 뒤로 5개의 블록이 더 붙어야 한다는 것이다. 그 정도로 많은 블록이 추가로 붙은 체인이어야만 메인 체인으로 인정을 해 줄수 있기 때문이다.

거꾸로 말하면 비트코인은 60분동안은 내 거래내역이 무효화 될 수 있다는 뜻.

 

하지만,

갓레이튼은 Finality까지 1초면 충분하다.

 

 

 

3. fork(분기발생)

비트코인과 이더리움은 PoW 알고리즘으로 블록 생성 노드를 결정한다.

매우 복잡한 연산을 노드들끼리 경쟁적으로 하며 먼저 문제를 푼 노드가 블록을 생성하고, 주위 노드들로 자신이 문제를 풀었다고 전파를하는 원리인데,

 

만약 두개의 노드가 기가막힌 타이밍으로 동시에 문제를 푼다면?

그러면 블록체인에 분기가 발생한다.

 

하나의 예시 상황을 보자

  1. 내가 비트코인을 친구에게 전송했다.
  2. 내가 전송한 거래 기록은 트랜잭션 풀에 저장된다.
  3. 채굴 노드들은 트랜잭션 풀에서 거래 기록을 모으고 문제를 풀며 블록을 생성하기 위해 애쓴다.
  4. 이때 기막힌 타이밍에 A노드와 B노드가 동시에 문제를 풀어냈다.
  5. 따라서 A노드와 B노드는 동시에 자신이 문제를 풀었다는 사실을 주위 노드들에게 전파한다.
  6. 주위 노드들은 A노드에게 전파받은 노드는 A노드가 생성한 블록을 체인에 추가하고 다시 문제를 푼다.(B노드의 경우도 마찬가지) 이때 A노드와 B노드가 동시에 생성한 블록의 내용은 동일하다. 동일한 트랜잭션을 모았으니까.
  7. 그러면 현재 A노드가 생성한 #6-1블록이 체인 마지막에 붙은 줄 알고있는 노드가 있을 것이고, B노드가 생성한 #7블록이 체인 마지막에 붙은 줄 알고있는 노드가 있을 것이다.
  8. 이 상태로 노드들은 다시 열심히 채굴을 하고 이번에는 어떤 C노드가 문제를 풀어내어 블록 생성 권한을 가졌다고 해보자.
  9.  C노드는 B노드가 생성했던 #7블록을 마지막으로 하는 체인을 알고있는 노드라면 #7블록 뒤에 자신이 생성한 #8 블록을 붙인다. 그리고 이 사실은 모든 노드들에게 전파된다.
  10. #6-1 블록을 갖고있던 노드들은 #7블록이 없기에 자신들의 체인을 포기하고 #6 - #7 - #8 체인의 형태로 다시 채굴을 시작한다.

 

이런 흐름으로 PoW가 작동하는데 여기서 51%공격이 발생할 수 있다.

 

황레이튼은 이런 위험이 있는 PoW 방식 대신 IBFT 알고리즘을 사용하여 문제를 해결했다.

 

https://steemit.com/kr/@kanghamin/istanbul-byzantine-fault-tolerance

 

Istanbul Byzantine Fault Tolerance/이스탄불 비잔티움 장애 허용 — Steemit

Istanbul Byzantine Fault Tolerance(=IBFT)에서는 3단계로 합의과정이 이루어져있다. Pre-Prepare Prepare Commit 시스템은 잘못된 노드(나쁜 노드)가 F라고 가정했을때 총 노드수가 3F+1이상이면 돌아갈 수 있다. N =

steemit.com

 

 

 

 


2. 클레이튼의 합의 과정

참여 노드 수를 제한하여 성능을 높힌 BFT 알고리즘은 덜 분산적이고 투명성이 저하된다는 단점이 있다.

이 단점을 개선한 방식이 IBFT다. 이거에 대해선 따로 다시 공부를 해야한다...

 

IBFT의 경우 1명의 제안자(Proposal)다수의 위원회(Commit/Validator)로 구성된다.

제안자는 거버넌스 카운실(Governance Council) 노드들 중 랜덤으로 선정된 하나의 노드이며 이번 라운드는 자신이 선정다는 사실을 자신의 공개키를 통해 암호화하여 위원회 노드들에게 알린(전파한)다. (클레이튼의 블록 생성 주기를 라운드라고 한다.)

위원회 노드들의 경우도 자신이 합의 노드라는 것을 합의 노드들에게 공개키를 통해 암호화하여 알린다.

제안자는 합의 노드의 2/3의 서명을 받을 경우 블록을 생성하고 다른 위원회 노드들에게 전파한다.

 

 

 

 


3. 클레이튼 네트워크의 구조

CNN : consensus node network = 합의 노드 / 제한 노드

PNN : proxy node network = 블록을 앤드포인트 노드에게 전달하는 노드

ENN : Endpoint node network = 기타 노드 / 누구나 참여가능 노드

CN/PN/ENBootnode : 새로운 참가자의 노드를 등록하게 도와주는 노드

CNN + PNN : Core cell

출처: https://jeongbincom.tistory.com/88 [하나셋 - 프로그래머로써 살아남기]

CN노드들간의 합의 끝에 블록이 생성되고 CNN에서 PNN으로, PNN에서 EN들에게 전파된다.

 

 

왜 CN -> EN의 다이렉트 통신을 안하고 CN -> PN -> EN처럼 중간에 PN을 둘까?

위에 언급하였듯 블록체인 네트워크에서 커다란 request가 들어온다면 이 request를 여러 노드들이 분산하여 처리하는 것이 아닌 각각의 노드들이 모든 request를 처리해야한다. 따라서 네트워크의 성능은 그 네트워크에서 가장 낮은 성능의 노드에 맞춰진다.

 

즉, CN 노드들의 성능이 매우 중요한데, CN 노드들은 합의하는 것에만 최선을 다해도 모자란 판인데 EN들과 통신하는 것에 자원을 낭비해선 안된다. 따라서 CN 하나에 여러개의 EN을 연결하는 구조 대신, CN 하나와 여러개의 EN 사이에 PN을 두어 EN과 통신하는 측면에서 성능을 높힌것이다.

 

참고로 CN 노드가 되려면 

  • Physical core가 40개 이상
  • 256GB RAM
  • 14TB 이상 저장공간
  • 10G 네트워크

의 조건이 필요하다고 한다.

 

 

 

 


4. 서비스 체인

메인넷과 연결돼있고, 독립적으로 운영되는 체인이다.

  • Bapp이 특별한 노드 환경에서 수행되어야 할 때
  • 프라이빗 블록체인과 같이 보안 수준 맞춤형으로 설정해야 할 때
  • 많은 처리량을 요구하는 서비스라서 메인넷에서 사용하기엔 비용 부담이 될 때

서비스 체인을 사용한다.

 

메인체인과 서비스체인간 통신은 되지만 제한된 조건 하에서 통신한다.

요약하면 서비스 체인은 독립된 서비스 공간을 구축해서 필요할때 메인넷의 신뢰를 고정 시키는 것

참고로 클레이튼의 서비스 체인에서는 트랜잭션 가스비를 안받도록 설정할 수 있다.

 

 

 

 


5. 이더리움 VS 클레이튼

이더리움

  • 단일 네트워크
  • PoW방식이고, 어떤 노드가 블록을 생성하게 될지 모름
  • 따라서 최신 블록을 전파받기 위해서 노드는 다른 모든 노드들고 최대한 연결돼 있어야한다.

 

클레이튼

  • 2개의 레이어를 가지는 네트워크
  • 매 라운드마다 합의 노드들 중  하나가 뽑혀서 블록을 생성한다.
  • 이더리움과 달리 합의 노드들 중에서 블록이 생성되기에 어떤 노드에서 생성될지 거의 알 수 있다.
  • 따라서 최신 블록을 전파받기 위해서는 합의 노드들에게만 연결해 있으면 된다.
  • 개발자가 Bapp을 만들고 엔드포인트 노드에 접속한다. EN -> PN -> CN
  • 혹은 개발자의 Bapp 자체를 엔드포인트로 활용하여 네트워크에 접근할 수도 있다.
  • 메인넷과 부분적으로 소통하며 독립된 서비스 공간을 구축할 수 있는 서비스 체인이 있다.

 

 

 

 

 

참고:

더보기

https://media.fastcampus.co.kr/insight/why-blockchain-is-hard/

블록체인에 대해 전반적인 그림을 그리기 위해 조사하며 얻은 얕은 지식입니다.


1. DID란?

  • 탈중앙화 신원증명(Decentralized Identity)이다.
  • 개인 정보를 사용자 개인이 보관하여(개인 소유의 단말기 등에) 개인 정보 인증 시 필요한 정보들만 골라서 인증을 진행할 수 있도록 해주는 전자신원 증명기술이다.
  • 기존에는 기업이 사용자의 모든 개인 정보를 중앙집중형으로 관리하는 형태였지만, DID는 자신의 개인정보를 자신이 직접 관리하는 분산형태이다.
  • 사용자들의 개인 정보는 사용자들의 개인 단말기에 저장되고, 블록체인에 존재하는 DID 문서라는 것을 통해서 개인 정보를 제출하여 인증받을 수 있다.

 

 

 


2. 왜 DID?

기존 인증 시스템만으로도 세상은 잘 돌아갔다. 굳이 이런 어려운 기술을 사용하는 이유는 뭘까?

 

1. 개인정보 관리의 위험

https://www.thedailypost.kr/news/articleView.html?idxno=80609

 

 

 

"유저 데이터를 소유하는 것은 자산에서 부채가 되고있다"

-비탈릭 부테린-

 

 

이런 이슈를 DID는 해결해줄 수 있다.

  • 개인의 입장에선, 자신의 정보를 직접 자신이 직접 관리하기에 나의 개인 정보를 기업에 의존하지 않아도 되고,
  • 기업의 입장에선, 직접 관리했다가 유출사고가 발생할 수 있는 리스크를 줄일 수 있다.

그러면 DID 기술을 어떻게 구현되는지 살펴보자.

 

 

 

 


3. DID의 구성 요소

1. DID 아이디

DID 아이디는 DID 문서의 위치를 나타낸다. 콜론을 통해서 3등분으로 구분되는데 의미는 다음과 같다.

  • did: DID 스키마를 따를 것임을 의미
  • example: DID 메소드의 이름을 의미, DID 메소드란 특정 분산 원장 또는 네트워크에서 DID와 관련된 DID 문서들을 생성, 읽기, 갱신, 그리고 비활성화 하는 메커니즘이다. DID 메소드들은 여기에서 확인해볼 수 있다.
  • 123456789abcdefghi: DID 메소드 안의 고유 아이디를 의미한다.

 

 

2. DID 문서

DID문서는 DID아이디의 제어권, 소유권을 증명할 수 있는 공개키와 인증정보를 가지고있다. 블록체인에 올라가있으며 여기에는 민감한 개인정보는 포함돼 있지 않다.(블록체인에 보관하지 않아도 되지만 대부분 블록체인에 보관한다고 한다.)

  • id: 이 DID문서가 설명하고 있는 아이디를 의미한다.
  • publicKey: 아이디와 관련된 공개키 리스트.
  • authentication: 이 아이디의 소유권 증명을 위한 정보.
  • service: 이 아이디와 상호작용이 가능한 서비스들을 의미한다.

 

 

3. VC(Verifiable Credential)

블록체인에 올라가서 모두에게 보여지는 DID문서에는 당연히 사용자의 개인정보가 포함되면 안된다. 사용자의 민감한 정보들은 사용자가 소지하는 단말기(휴대전화 등)에 저장되며 이를 VC라고 한다. 

  • VC에는 이름, 나이, 주소, 백신접종여부등의 정보가 들어있다.
  • VC는 개인이 단말기의 지갑 애플리케이션과 같은 안전한 영역에서 보관한다.
  • 자신이 가진 모든 VC들을 인증을 위해 제출해야 하는것이 아닌, 원하는 VC들만 골라서 제출할 수 있다. 즉, 담배를 구입할 때 신분증을 제출하면 나의 이름과 주민등록번호와 주소지 등이 노출되지만, 성인여부에 대한 VC만 골라서 제출하면 다른 민감한 정보의 노출을 막을 수 있다.
  • VC의 단위는 Claim이라고 한다.

 

 

 

 


4. 인증 과정

모든 종류의 DID가 다음과 같은 인증 절차를 가지는 것은 아니기에 거시적인 관점에서만 DID발급, 사용, 검증으로 나누어서 살펴보도록 하자.

 

 

1. 발급하는 법

신원정보발행자가 어떤 유저의 클레임을 발급해주는 과정이다.

  1. 사용자는 신원정보발행자(이슈어)에게 클레임(인증하고자 하는 항목, 예를들어 백신접종여부) 발급을 요청한다.
  2. 공인 인증된 이슈어는 사용자의 클레임을 생성하고 클레임에 이슈어의 개인키로 서명을 한다.
  3. 그 후 이슈어는 블록체인에 존재하는 사용자의 DID문서에 이슈어의 공개키를 등록한다.
  4. 발급된 클레임을 사용자에게 반환한다.

이제 사용자는 어떤 권한에 대해 인증할 수 있는 하나의 클레임을 얻은 것이다.(성인 여부, 백신 접종여부 등)

 

 

 

2. 사용하는 법

사용자가 발급받은 클레임을 통해서 자신의 정보를 인증하는 과정이다.

  1. 사용자는 발급받은 클레임에 자신의 개인키로 서명을 한다. (클레임은 현재 사용자의 개인키로 1회, 이슈어의 개인키로 1회, 총 두번의 개인키 서명이 된 상태)
  2. 사용자의 공개키를 블록체인에 존재하는 자신의 DID 문서에 등록한다. (혹은 클레임과 함께 공개키도 직접 보낸다)
  3. 검증자에게 클레임을 제출한다.

여기서 검증자는 백신패스를 확인하는 음식점 사장님이라고 가정해보자.

 

 

 

3. 검증하는 법

  1. 음식점 사장님은 사용자의 클레임을 받아서 검증을 진행한다.
  2. 검증은 클레임에 걸린 두번의 개인키 서명을 풀어야한다(복호화)
  3. 먼저 사용자의 DID문서를 조회하여 사용자의 공개키를 획득하고(혹은 직접 받은 공개키를 획득하고),
  4. 사용자가 전달한 클레임에서 사용자가 개인키로 서명한 것을 복호화한다.
  5. 사용자의 개인키로 된 서명이 풀렸다는 것은 -> 인증 정보가 해당 사용자의 것이 맞다는 것을 의미한다.
  6. 다음으로, 사용자의 DID문서에 존재하는 이슈어의 공개키를 통해서 이슈어의 개인키로 서명한 것을 복호화한다.(이제 모든 서명이 풀렸다!)
  7. 이슈어의 공개키로 복호화가 되었다는 것은, 해당 이슈어가 발급한 클레임이 맞다는 것이고, 이는 사용자의 인증 정보가 신뢰할 수 있음을 의미한다.

 

 

 

 

 

 

참고:

더보기

https://media.fastcampus.co.kr/insight/why-blockchain-is-hard/

블록체인에 대해 전반적인 그림을 그리기 위해 조사하며 얻은 얕은 지식입니다.


1. 채굴이란?

채굴은 새로운 블록을 생성하는 것을 의미한다.

블록은 채굴 노드가 생성한다.

저번 포스팅에서 말했 듯, 트랜잭션들은 검증노드로부터 네트워크 여러곳으로 전파된다.

채굴노드는 네트워크에 공유되고 있는 트랜잭션들을 모아서 블록 만들기를 시도한다.

 

 

비트코인은 블록 한개당 1MB의 크기이다. 물론 모든 코인이 동일하지는 않고 어떤 블록체인을 사용하느냐에 따라 다르다.

 

 

비트코인은 블록 한개가 생성되는데 10분이 걸린다. 즉, 나의 거래내역이 블록에 포함되어 체인에 붙기까지 10분이 걸린다는 것이다. 또 컨펌의 과정을 위해서 추가로 6개의 블록이 이어져야한다.(비트코인의 경우)

컨펌이란 나의 트랜잭션이 포함된 블록이 블록체인의 메인 체인에 들어있다고 확정하는 것이다.

 

 

 

 


2. 어떤 채굴노드가 블록을 생성하는가?

채굴 노드들은 블록을 생성하기 위한 경쟁을 서로 한다.

모든 노드들이 블록을 생성하면 체인은 너무 많은 분기(갈래)가 발생할 것이다.

이 경쟁은 수학문제를 푸는 것이며, 정확히는 특정 해쉬값이 나오게 하는 임의의 논스(nonce)값을 먼저 찾는 경쟁이다.

이 경쟁에서 승리하면 블록을 생성할 수 있고 이 노드에게는 보상이 주어진다.

논스값을 찾는 것은 CPU보다는 GPU를 사용하는 것이 훨씬 빠르기에 그래픽카드를 사용한 채굴이 많은 것이다.

아무튼 이런 고생을 PoW 알고리즘이라고 하며 모든 블록체인이 이 방법을 사용하는 것은 아니다.

여러가지 채굴 알고리즘이 존재하는데, 다음 포스팅에서 공부해보겠다.

 

 

 

 


3. 동시에 블록이 만들어진다면?

경쟁을 하였음에도 동시에 블록을 생성하여 분기가 발생한다면?

 

6번 블록 이후 두개의 갈래로 나눠졌다.

이때 6-1블록과 7블록 중 어떤 블록쪽으로 계속 이어나가야 할까? 

이때 위에 언급한 확정(컨펌)의 개념이 나온다.

 

PoW알고리즘은 

 

"네트워크에 더 많은 기여를 한 사용자는 신뢰할 수 있는 사용자다"

 

라는 전제를 하고있다.

따라서 최초로 분기가 발생하고 그 뒤로 더 많은 블록들이 붙은 쪽이 더 많은 기여를 받은 분기이고 이 쪽을 메인 체인으로 인정한다. 블록들을 더 붙혀보고 많이 붙은 분기를 선택하는 이 과정이 위에서 언급한 컨펌(확정)의 과정이다.

인정받지 못한 분기의 블록들은 고아블록(스테일블록)이 된다.

 

 

 

 


4. 51%공격

블록을 수정하거나 조작하는 것은 불가능할까?

채굴하는 과정에서 GPU는 많은 전력을 소모한다. 논스값을 찾는 경쟁을 하기 때문인데,

왜 채굴하는데 이렇게 많은 전력을 쓰도록 설계한 것일까?

그냥 다음 블록을 생성할 노드를 랜덤으로 정하면 안되는 것일까?

 

이유는 블록체인 네트워크를 해킹으로부터 보호하기 위함이다.

블록 1개를 만드는데 많은 고생(채굴)을 했으니 이것을 수정하기 위해서는 그 이상의 고생(컴퓨팅 파워)를 하도록 만든 것이다. 또 블록 하나를 조작하면 그 블록의 해시값이 바뀌고 이로인해 연결된 블록들의 해시값도 바뀌기 때문에 사실상 블록체인 네트워크 전체 노드들의 고생보다 더 큰 고생이 필요하다.

 

하지만 그렇다해도 방법은 존재한다.

51% 공격이라고 불리는 수단인데, 과정은 다음과 같다.

 

 

 

1. A가 B에게 30코인을 줬다는 거래 내역이 101번 블록에 기록됐다.

 

 

2. A는 전체 네트워크의 51%이상의 컴퓨팅 파워를 지니고 있다고 가정한다.

3. A는 자기의 거래내역을 없애버린 새로운 블록을 만들고 그 뒤로 컨펌을 위한 블록들을 더 빠르게 생성한다.(자신이 가진 51% 이상의 압도적인 컴퓨팅 파워를 사용해서)

 

 

4. 이렇게 되면 A가 조작한 블록이 속한 분기가 메인 체인으로 인정된다. 

고로 A가 B에게 코인을 보낸 사실은 사라진다.

 

 

 

 


5. 51% 공격 대응

사실 이 공격은 공격자가 손해를 보는 모순을 가지고있다.

 

첫째로,

공격자가 이득을 취하기 위해서는 해당 암호화폐의 가치가 올라야한다.

하지만 이런 공격으로인해 해당 암호화폐에 대한 신뢰가 떨어지게 되고, 가치도 떨어진다.

그렇다면 51% 소유자가 가장 큰 손해를 보는 상황이 돼 버린다.

실제로 2014년 특정 마이닝풀이 일시적으로 코인의 해시파워가 51%를 초과하는 일이 발생했을 때, 가격이 폭락했다. 즉, 이론적으로 가능하나 경제적인 이유로 불가능한 공격이라는 것이다.

 

둘째로,

블록체인 네트워크의 참여자가 많을수록 51%의 해시파워를 확보하는 것은 매우 높은 비용이 필요하다.

따라서 해킹을 통해 얻는 수익보다 적자가 난다는 것이다.

하지만 참여자가 적은 네트워크는 51%의 파워를 확보하고 유지할 수도 있기 때문에 이 부분에서는 대안이 필요하다.

이것도 다음 포스팅에서 공부해보겠다.

 

 

 

 

 

참고:

더보기

+ Recent posts