가위바위보 스마트 컨트랙트 게임을 배포하고 난 후 문제점이 발견됩니다. 새로운 방을 생성한 방장이 입력한 조건값이 트랜잭션에 그대로 노출된다는 점입니다. 스마트 컨트랙트에서 발생한 트랜잭션을 뜯어보면 input Data 부분에 방장이 입력한 값이 버젓이 나오고 있습니다. 이에, 참여자들은 방장이 입력한 값을 보고 이길 수 있는 조건값을 걸어 무한정 이길 수 있게 됩니다.
이를 방지하기 위해서는 입력값을 외부에 노출되지 않도록 해야 합니다. 이 때 필요한 함수가 바로 해시 함수입니다.
입력값 숨기기
방장의 조건값을 외부에 노출하지 않으면서 가위바위보 게임 로직을 그대로 유지하기 위해서는 해시된 값을 입력하고, 게임의 결과를 판독할 때 복호화가 이뤄져야 합니다.
createRoom과 joinRoom에서는 암호화된 해시값이 입력되고, compareHands나 payout() 함수에서는 복호화가 이뤄져야 합니다.
① keccak256 해시 함수
솔리디티에서는 해시함수로 keccak256() 함수가 사용됩니다. bytes32형 데이터를 반환하며, 입력값으로는 abi.encodePacked()로 인자를 바이트 타입의 변수로 인코딩 한 값을 사용합니다.
function keccak(uint256 _hand, string memory _key) public pure returns (bytes32) {
return keccak256(abi.encodePacked(_hand,_key));
}
② verifyOriginator 함수
결과값을 확인하기 위해서 comparesHand()함수 내에서 verifyOriginator()함수를 사용합니다. 즉 가위바위보 입력값과 key값을 받아 해당 조건값을 입력한 참여자가 맞는지 해싱된 값을 사용합니다.
function verifyOriginator(uint256 _hand, string memory _key, uint roomNum) private returns(bool) {
require(msg.sender == rooms[roomNum].originator.addr);
if(keccak(_hand, _key) == rooms[roomNum].originator.hashHand){
rooms[roomNum].originator.hand = Hand(_hand);
return true;
}
else { return false; }
}
③ 구조체 변경
참여자들은 가위바위보를 그대로 사용하는 것이 아니라 해시된 가위바위보 값을 가지게 됩니다.
struct Player{
address payable addr;
uint256 playerBetAmount;
Hand hand;
// New code
bytes32 hashHand;
PlayerStatus playerStatus;
}
입력값 숨기는 방법
① 게임의 작동 순서는 먼저 참여자 혹은 방장은 내고자 하는 가위바위보를 해싱한 값을 keccak 함수를 통해 구하게 됩니다.
② 해싱된 값을 가지고 방을 생성하고, 참여자들은 방에 참여해 베팅을 진행합니다.
해당 방의 잔액에는 1ETH가 들어있습니다.
트랜잭션을 확인해보면 방장이 입력한 값(가위를 냈습니다)이 해시값으로 암호화 되어 있어 확인할 수 없습니다. 해당 값을 확인하기 위해서는 해싱할 때 들어갔던 key값을 알아야 합니다.
③ 새로운 참여자가 방에 참여합니다.
④ compareHands() 함수로 결과를 확인하고 이더를 분배합니다.
추가 기능 구현하기
① 방에 참여자들 카운팅
방의 번호를 입력받아 해당 방에 참여한 플레이어의 수를 반환합니다. 블록체인에서 아무런 데이터도 변형하지 않으므로 view 함수로 작성합니다.
function checkNumOfPlayer(uint roomNum) public view returns(uint numOfPlayers){
return rooms[roomNum].playerNum;
}
특정 방에 참여한 플레이어 수를 반환합니다.
② 가장 많은 베팅 금액 방 찾기
방의 정보를 담은 구조체형 변수 rooms을 참조합니다. 가장 많은 베팅 금액을 담고 있는 방을 찾기 위함이므로 인수는 받지 않으며 roomNum을 반환합니다.
function checkMaxBetRoom() public view returns(uint8 MaxBetRoom){
uint8 max = 0;
uint8 maxBetRoom = 0;
for(uint8 i=0; i<roomLen; i++){
if(rooms[i].betAmount > max){
max = uint8(rooms[i].betAmount);
maxBetRoom = i;
}
}
return maxBetRoom;
}
가장 많은 베팅 금액을 가진 방 번호를 반환합니다.
Reference
전체 소스코드 : Github
스마트 컨트랙트 : Etherscan (0x9d363fF4a841E596D0783D8106bf8B960Ee3F4f6)
'Blockchain' 카테고리의 다른 글
[Blockchain] 이더리움 토큰 발행하기 (ERC-20 라이브러리 사용) (0) | 2022.07.14 |
---|---|
[Blockchain] 이더리움 토큰 발행 ERC-20 이란? (0) | 2022.07.14 |
[Blockchain] 가위 바위 보 스마트 컨트랙트 만들기 (0) | 2022.07.13 |
[Blockchain] Remixd 사용하는 방법 (0) | 2022.07.12 |
[Blockchain] 이더리움 스마트 컨트랙트 배포하기(Remix) (0) | 2022.07.12 |
댓글