블라인드 경매 시스템 설계하기

KimCookieYa·2022년 8월 6일
0

블록체인 인 액션

목록 보기
3/5
post-thumbnail

* Blockchain in Action(블록체인 인 액션) 책의 실습을 바탕으로 작성하였음.

설계 단계

해결할 문제를 살펴보고 설계 원칙을 적용해 본다. 데이터 구조를 정의하는데 설계 원칙 2와 설계 원칙 3을 사용한다. 컨트랙트 다이어그램과 유한 상태 머신(FSM)을 사용해 설계를 형상화한다. 스마트 컨트랙트에 수정자를 사용한다. 설계 원칙을 적용함으로써 블록체인 솔루션을 구조적인 접근 방법으로 개발할 수 있다.


설계

솔리디티 문서에 나온 블라인드 경매 예제를 실습하며, 프라이버시와 보안 이슈에 관해 초점을 맞추도록 한다.

문제 설정

어떤 예술 작품을 블라인드 경매로 판매해야 한다. 여러 작품을 경매로 판매할 수 있겠지만, 여기서는 오직 한 작품만 판매한다고 가정하자. 이 작품이 팔린 후 다른 작품을 추가할 수는 있다. 수혜자는 경매의 여러 단계 {Init, Bidding, Reveal, Done}를 관리한다. 수혜자가 경매를 시작하고 나면, Bidding 단계 동안 입찰자는 한 번에 하나씩 입찰을 한다. 여기서 입찰은 보안이 보장되며 프라이빗하게 이루어져야 한다. 수혜자를 포함한 다른 사람은 각 입찰의 내용을 볼 수 없다. 일정 시간 후, 수혜자는 Reveal 단계로 진행시킨다. 이제 입찰자는 각자의 입찰 내용을 공개하고, 수혜자는 전체 입찰 내용을 공개해서 가장 높은 가격을 제시한 입찰자와 입찰가를 찾아낸다. 수혜자는 단계를 Done으로 변경하고 경매를 종료한다. 수혜자의 어카운트로 최고가 입찰가를 전송한다. 경매에 떨어진 입찰자는 그들의 예치금을 출금할 수 있으나, 낙찰받은 입찰자는 입찰한 금액을 출금할 수 없다.

다이어그램 설계

경매의 state는 투표의 state와 유사하다. 블라인드 경매는 Bidding 단계에서 일어난다. 모든 입찰에는 예치금이 필요하다. 이 예치금은 입찰금보다 커야 한다. 모든 입찰이 끝난 다음, 입찰자들은 Reveal 단계에서 다시 한번 그들의 입찰 내용을 전송해야 하는데, 이번에는 그 내용을 오픈해야 한다. 낙찰자는 Reveal 단계에서 결정된다.

BlindAuction의 FSM

BlindAuction의 컨트랙트 다이어그램

프라이버시와 보안

블라인드 경매에서 Bidding 단계 동안, 입찰자는 블라인드 입찰을 하므로 입찰 내용(값)은 secure할 뿐만 아니라 private하다. 어떻게 이러한 프라이버시와 보안을 보장할 수 있을까? 본 실습에서 사용하는 해싱 함수는 Keccak 256비트 해시 함수이다. 입찰가인 정수 20의 Keccak 해시값은 32바이트 데이터이므로, 이것만 보면 무슨 정보인지 알 수 없다. 그러나 메시지 내용에 대한 대략적인 정보를 가지고 있고, 경매 아이템의 가격대를 알고 있다면, 무작위 대입(Brute Force)으로 해시 전의 원본값을 쉽게 해독해낼 수 있다.

이로부터 보안을 지키기 위해, 논스(Nonce)나 시크릿 패스워드를 두 번째 파라미터로 사용할 수 있다. 이 시크릿은 직불카드의 개인 핀 번호 같은 존재다. 어떤 값을 Keccak으로 해시할 때 두 번째 시크릿 패스워드를 함께 묶어서 계산하면, 무작위 대입 공격에 대해 안전해진다.

keccak256[abi.encodePacked(20)]
keccak256[abi.encodePacked(20, 0x426526)]

이러한 해싱은 프라이버시와 보안이 필요한 경우에 적용 가능한 간단한 기법이다.

BlindAuction.sol 코딩

앞의 설계 내용을 코드로 전환해 본다. 블라인드 경매 문제는 두 가지의 주요 규칙을 가지고 있는데, 수혜자가 경매의 시작, 종료, Bidding, Reveal 단계 시점을 결정하고, 오직 수혜자만이 한 단계에서 다음 단계로 변화시킬 수 있다는 것이다. 이러한 조건을 수정자 validPhase와 onlyBeneficiary로 구현했다.

Reveal 단계 전까지 모든 유효한 입찰을 제출해야 한다. Reveal 단계에서 입찰자는 입찰가와 시크릿 패스워드를 공개한다. 패스워드를 공개해도 되는 이유는 입찰자가 선정한 일회용 패스워드이기 때문이다. 스마트 컨트랙트 함수 reveal()은 제출한 입찰가와 시크릿 패스워드를 이용해 해시값을 계산하고, 이것이 블라인드 입찰에서 제출했던 해시값과 일치하는지 확인한다. 만일 해시가 일치하면 컨트랙트는 해당 입찰(placeBid() 함수)을 승인하고, 이것이 최고가인지 평가한다. 이러한 블라인드 입찰 확인 과정은 if 문으로 구현한다.

pragma solidity >=0.4.22 <=0.6.0;

contract BlindAuction {

    struct Bid {
        bytes32 blindedBid;
        uint deposit;
    }

    enum Phase {
        Init, Bidding, Reveal, Done
    }
    Phase public state = Phase.Init;

    address payable beneficiary;
    mapping(address=>Bid) bids;

    address public highestBidder;
    uint public highestBid = 0;

    mapping(address=>uint) depositReturns;

    modifier validPhase(Phase reqPhase) {
        require(state == reqPhase);
        _;
    }

    modifier onlyBeneficiary() {
        require(msg.sender == beneficiary);
        _;
    }

    constructor() public {    
        beneficiary = msg.sender;
        state = Phase.Bidding;
    }

    function changeState(Phase x) public onlyBeneficiary {
        if(x < state) revert();
        state = x;
    }
    
    function bid(bytes32 blindBid) public payable validPhase(Phase.Bidding) {  
        bids[msg.sender] = Bid({
            blindedBid: blindBid,
            deposit: msg.value
        });
    }
    
    function reveal(uint value, bytes32 secret) public validPhase(Phase.Reveal) {
        uint refund = 0;
            Bid storage bidToCheck = bids[msg.sender];
            if (bidToCheck.blindedBid == keccak256(abi.encodePacked(value, secret))) {
                refund += bidToCheck.deposit;
                if (bidToCheck.deposit >= value) {
                    if (placeBid(msg.sender, value))
                        refund -= value;
                }
            }
            
        msg.sender.transfer(refund);
    }

    function placeBid(address bidder, uint value) internal returns (bool success) {
        if (value <= highestBid) {
            return false;
        }
        if (highestBidder != address(0)) {
            depositReturns[highestBidder] += highestBid;
        }
        highestBid = value;
        highestBidder = bidder;
        return true;
    }

    function withdraw() public {   
        uint amount = depositReturns[msg.sender];
        require (amount > 0);
        depositReturns[msg.sender] = 0;
        msg.sender.transfer(amount);
    }
    
    function auctionEnd() public validPhase(Phase.Done) {
        beneficiary.transfer(highestBid);
    }
}

테스팅




베스트 프랙티스

지금까지 블라인드 경매에서 파라미터를 시크릿 코드나 패스워드를 포함해 해싱함으로써 입찰 내용을 난독화했는데, 이와 같이 해싱 기법을 사용해 데이터의 프라이버시와 보안을 확보할 수 있다.

  • 탈중앙화 블록체인 기반 시스템에서 전송되는 데이터의 프라이버시와 보안을 확보하기 위한 해싱 기법을 잘 활용해야 한다.
profile
무엇이 나를 살아있게 만드는가

0개의 댓글