iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 7
0

導言

此範例針對藝文活動售票情境,每日有限制售票票數,入門前需要驗票,單日無限次入場;在合約設計方面,加入售票結束時間,保有單日最高參與者數量(限制票數),不過不限制單人買票數量,所以每天都要發布新的CONTRACT代表當天售票狀況,買票時FUNCTION會有兩個判斷機制(時間、參與者數量);再來是驗票的部分,每個人買票時都會產生一筆HASH VLAUE,假設活動現場採用QR CODE掃描驗證票券真假,就是驗證HASH VALUE有沒有在address購買的集合中

發想參考Yuren Ju大大和KK Chen大大的報名系統

程式碼

pragma solidity^0.4.25;
//以finney為單位,1 finney == 0.001 ether
contract Ticket{
    address public Host; //發起人-主辦單位
    uint256 public TicketPrice; //當日票價
    uint256 public MaxParticipants; //當日票數
    uint256 public SalesTime; //當日販售結束時間
    uint256 public SalesDate; //當日販售日期

    uint256 public amountOfParticipant;
    mapping(address => bytes32[]) public tickets;
    mapping(uint => address) public IdToUser;

    constructor(uint256 _price, uint256 _participants, uint256 _time) public{
        Host = msg.sender;
        TicketPrice = _price*10**15;
        MaxParticipants = _participants;
        SalesTime = now + _time;
        SalesDate = now;
    }

    //買票
    function () external payable{
        buyTikcet();
    }

    //買票邏輯
    function buyTikcet() private{
        require(now <= SalesTime,"超出時間"); //確認有無超過販售時間
        require(amountOfParticipant < MaxParticipants,"今日票價已售完"); //確認沒有超過總售票量
        require(msg.value == TicketPrice,"不符合票價"); //確認支付的錢跟售價一樣
        amountOfParticipant++;
        
        //插入轉換hash function
        bytes32 ticketHash = keccak256(abi.encodePacked(amountOfParticipant,msg.sender)); //將userId和user address一同hash
        tickets[msg.sender].push(ticketHash); //紀錄
        IdToUser[amountOfParticipant] = msg.sender;
    }

    //驗票
    function verify(bytes32 _ticketHash) public view returns(bool){
        require(now <= SalesTime,"超出時間");
        for(uint i = 0 ; i < tickets[msg.sender].length ; i++){
            if(_ticketHash == tickets[msg.sender][i]){
                return true;
            }
        }
    }

    //主辦提款
    function withdraw() public{
        Host.transfer(address(this).balance);
    }
    
}

解說

address public Host; //發起人-主辦單位
uint256 public TicketPrice; //當日票價
uint256 public MaxParticipants; //當日票數
uint256 public SalesTime; //當日販售結束時間
uint256 public SalesDate; //當日販售日期


uint256 public amountOfParticipant; //參與者數量
mapping(address => bytes32[]) public tickets; //value為byts32陣列,一個address可以買多張票,每個票都有一個tickets hash
mapping(uint => address) public IdToUser; //票券ID對應的購買者

constructor(uint256 _price, uint256 _participants, uint256 _time) public{
        Host = msg.sender;
        TicketPrice = _price*10**15;
        MaxParticipants = _participants;
        SalesTime = now + _time;
        SalesDate = now;
    }

發起合約depoly contract時,需要輸入當日售票票價(以finney為計價單位,1 finney == 0.001 ether)、當日票券總數量(以參與者數量計算)、販售時間

function () external payable{
        buyTikcet();
    }

function buyTikcet() private{
    require(now <= SalesTime,"超出時間"); //確認有無超過販售時間
    require(amountOfParticipant < MaxParticipants,"今日票價已售完"); //確認沒有超過總售票量
    require(msg.value == TicketPrice,"不符合票價"); //確認支付的錢跟售價一樣
    amountOfParticipant++;
    
    //插入轉換hash function
    bytes32 ticketHash = keccak256(abi.encodePacked(amountOfParticipant,msg.sender)); //將userId和user address一同hash
    tickets[msg.sender].push(ticketHash); //紀錄
    IdToUser[amountOfParticipant] = msg.sender;
}

  • 執行合約時沒有指定function就會自動執行fallback function並支付發起合約時所訂的價格,一次買一張票。

fallback functionexternal,代表只提供給外部call function,搭配payable執行時需要支付一筆ether;buyTicket() function 為private 代表只能在合約內部執行且不公開,當fallback function執行時,會執行buyTicket() function,判斷目前有沒有超過販售時間、有沒有剩餘的票券、支付的錢有沒有符合售票價格。通過條件判斷之後,增加參與者數量,以keccak256 hash function 將 參與者數量(也可以當作票券ID) 和買票的address 共同HASH,產生出32bytes的票券hash value,作為當日驗票的證明。最後,票券hash value 會push到 mapping tickets[msg.sender]紀錄在買票的address的bytes32 array

function verify(bytes32 _ticketHash) public view returns(bool){
    for(uint i = 0 ; i < tickets[msg.sender].length ; i++){
        if(_ticketHash == tickets[msg.sender][i]){
            return true;
        }
    }
}


function withdraw() public{
    Host.transfer(address(this).balance);
}
  • 驗票時輸入票券的HASH來辨別是不是由目前CALL FUNCTION的ADDRESS所購買。

verify()function 輸入票券hash,for loop會先根據mapping tickets[msg.sender]找到目前呼叫function的address,所擁有的票券hash,如果輸入的參數符合address擁有的票券之一,回傳true


上一篇
Day5- Guess Number Game
下一篇
Day7- SafePurchase
系列文
30天30個Smart contract 20
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言