以太坊智能合约源码解读(1)——排行榜

最近准备完全用智能合约实现一个系统,先看一些智能合约的源码学习一下。

源码github地址:https://github.com/Rello/BlockScores

该智能合约的基本功能

  • 注册新游戏
  • 增加玩家
  • 给每个玩家增加分数
  • 其他玩家可确认分数的增加,采用四眼原则 four-eyes principle
  • 管理玩家
  • 收取手续费
pragma solidity ^0.4.20;
/// @title Store lederboards in the Blockchain
/// @author Marcel Scherello [email protected]
/// @notice Create a custom leaderboard and start counting the scores
/// @dev All function calls are currently implement without side effects
/// @dev v1.1.0
contract BlockScores {
		//定义玩家结构体
    struct Player {
        bytes32  playerName;//玩家名称
        address playerAddress;//玩家地址
        uint  score;//分数
        uint  score_unconfirmed;//未确认分数
        uint   isActive;//玩家状态 1为激活 0为未激活
    }
    //定义榜单    
    struct Board {
        bytes32  boardName;//榜单名称
        string  boardDescription;//榜单描述
        uint   numPlayers;//玩家数量
        address boardOwner;//榜单拥有者
        mapping (uint => Player) players;//定义到玩家的映射
    }
    mapping (bytes32 => Board) boards;//定义到榜单的映射
    uint public numBoards;//榜单数量
    address owner = msg.sender;//合约拥有者地址

    uint public balance;
    uint public boardCost = 1000000000000000;
    uint public playerCost = 1000000000000000;
		
		//被该modifier标记的函数,只有合约拥有者可以调用
    modifier isOwner {
        assert(owner == msg.sender);
        _;
    }

    /**
    Funding Functions
    */

    /// @notice 提取所有金额给合约拥有者
    /// @return true
    function withdraw() isOwner public returns(bool) {
        uint _amount = address(this).balance;
        emit Withdrawal(owner, _amount);
        owner.transfer(_amount);
        balance -= _amount;
        return true;
    }

    /// @notice 改变使用合约的手续费
    /// @param costBoard 创建新榜单使用的手续费
    /// @param costPlayer 创建新玩家的手续费
    /// @return true
    function setCosts (uint costBoard, uint costPlayer) isOwner public returns(bool) {
        boardCost = costBoard;
        playerCost = costPlayer;
        return true;
    }

    /// @notice 在榜单拥有者和合约拥有者之间分配新玩家缴纳的手续费
    /// @param boardOwner 榜单拥有者
    /// @param _amount 待分配的费用
    /// @return true
    function split(address boardOwner, uint _amount) internal returns(bool) {
        emit Withdrawal(owner, _amount/2);
        owner.transfer(_amount/2);
        //emit Withdrawal(boardOwner, _amount/2);
        boardOwner.transfer(_amount/2);
        return true;
    }

    /// @notice 提现监听事件
    event Withdrawal(address indexed _from, uint _value);

    /**
    Board Functions
    */

    /// @notice 新增一个榜单. 榜单 hash值通过榜单名称和创建者生成
    /// @notice 创建新的榜单需要资金
    /// @param name The name of the leaderboard
    /// @param boardDescription A subtitle for the leaderboard
    /// @return The hash of the newly created leaderboard
    //新增一个榜单,返回榜单hash值
    function addNewBoard(bytes32 name, string boardDescription) public payable returns(bytes32 boardHash){
        require(msg.value >= boardCost);//创建者必须发送大于boardCost的ether,单位为wei
        balance += msg.value;//增加合约余额
        boardHash = keccak256(abi.encodePacked(name, msg.sender));//使用name和创建者address生成榜单hash
        numBoards++;//榜单数量加一
        boards[boardHash] = Board(name, boardDescription, 0, msg.sender);//创建榜单
        emit newBoardCreated(boardHash);//打印榜单哈希值
    }

    /// @notice 模拟榜单hash的生成
    /// @param name 榜单名称
    /// @param admin 榜单拥有者地址
    /// @return 返回可能的榜单hash值,因为榜单可能修改名称
    function createBoardHash(bytes32 name, address admin) pure public returns (bytes32){
        return keccak256(abi.encodePacked(name, admin));
    }

    /// @notice 获取榜单原数据
    /// @param boardHash 榜单hash值
    /// @return 榜单名称, 描述 和玩家数量
    function getBoardByHash(bytes32 boardHash) constant public returns(bytes32,string,uint){
        return (boards[boardHash].boardName, boards[boardHash].boardDescription, boards[boardHash].numPlayers);
    }

    /// @notice 榜单拥有者修改名称和描述
    /// @param boardHash 要被修改的榜单hash
    /// @param name 榜单新的名称
    /// @param boardDescription 榜单的新描述
    /// @return true
    function changeBoardMetadata(bytes32 boardHash, bytes32 name, string boardDescription) public returns(bool) {
        require(boards[boardHash].boardOwner == msg.sender);
        boards[boardHash].boardName = name;
        boards[boardHash].boardDescription = boardDescription;
    }

    /// @notice 创建榜单监听事件
    event newBoardCreated(bytes32 boardHash);


    /**
    Player Functions
    */

    /// @notice 玩家加入已存在的榜单
    /// @param boardHash 榜单hash
    /// @param playerName 玩家名称
    /// @return 返回成功或失败
    function addPlayerToBoard(bytes32 boardHash, bytes32 playerName) public payable returns (bool) {
        require(msg.value >= playerCost);//需要玩家转入大于等于playerCost的资金才能加入
        Board storage g = boards[boardHash];//获取榜单信息
        split (g.boardOwner, msg.value);//收入分成
        uint newPlayerID = g.numPlayers++;//玩家ID等于当前榜单玩家数量加1
        g.players[newPlayerID] = Player(playerName, msg.sender,0,0,1);//创建一个玩家
        return true;
    }

    /// @notice 根据榜单hash和玩家ID获取玩家信息
    /// @param boardHash 榜单hash
    /// @param playerID 玩家ID
    /// @return 返回玩家姓名, 已确认分数, 未确认分数
    function getPlayerByBoard(bytes32 boardHash, uint8 playerID) constant public returns (bytes32, uint, uint){
        Player storage p = boards[boardHash].players[playerID];//获取玩家信息
        require(p.isActive == 1);//玩家状态必须为1
        return (p.playerName, p.score, p.score_unconfirmed);
    }

    /// @notice 榜单拥有者可以移除玩家,就是将状态修改为0
    /// @param boardHash 榜单hash
    /// @param playerName 玩家名称
    /// @return true/false
    function removePlayerFromBoard(bytes32 boardHash, bytes32 playerName) public returns (bool){
        Board storage g = boards[boardHash];
        require(g.boardOwner == msg.sender);//必须是榜单拥有者
        uint8 playerID = getPlayerId (boardHash, playerName, 0);//根据名称获取玩家ID
        require(playerID < 255 );//玩家ID须小于255
        g.players[playerID].isActive = 0;//将玩家状态改为0
        return true;
    }

    /// @notice 根据榜单hash、玩家名称、玩家地址获取玩家ID
    /// @param boardHash 榜单hash
    /// @param playerName 玩家名称
    /// @param playerAddress 玩家地址
    /// @return ID or 999 in case of false
    function getPlayerId (bytes32 boardHash, bytes32 playerName, address playerAddress) constant internal returns (uint8) {
        Board storage g = boards[boardHash];
        for (uint8 i = 0; i <= g.numPlayers; i++) {
            if ((keccak256(abi.encodePacked(g.players[i].playerName)) == keccak256(abi.encodePacked(playerName)) || playerAddress == g.players[i].playerAddress) && g.players[i].isActive == 1) {
                return i;
                break;
            }
        }
        return 255;
    }

    /**
    Score Functions
    */

    /// @notice 给榜单玩家增加未确认分数. 或者覆盖已存在的未确认分数,这个合约
    /// @param boardHash 榜单hash
    /// @param playerName 玩家名称
    /// @param score 分数
    /// @return true/false
    function addBoardScore(bytes32 boardHash, bytes32 playerName, uint score) public returns (bool){
        uint8 playerID = getPlayerId (boardHash, playerName, 0);
        require(playerID < 255 );
        boards[boardHash].players[playerID].score_unconfirmed = score;
        return true;
    }

    /// @notice 将榜单玩家未确认分数变为已确认分数. 将未确认分数加到用户存在分数上. 玩家无法确认他自己的分数
    /// @param boardHash 榜单hash
    /// @param playerName 需要确认分数的玩家名称
    /// @return true/false
    function confirmBoardScore(bytes32 boardHash, bytes32 playerName) public returns (bool){
        uint8 playerID = getPlayerId (boardHash, playerName, 0);
        uint8 confirmerID = getPlayerId (boardHash, "", msg.sender);
        require(playerID < 255); // 玩家状态必须正常
        require(confirmerID < 255); // 确认玩家状态必须正常
        require(boards[boardHash].players[playerID].playerAddress != msg.sender); //需要由其他玩家来确认
        boards[boardHash].players[playerID].score += boards[boardHash].players[playerID].score_unconfirmed;//给待确认分数用户确认分数
        boards[boardHash].players[playerID].score_unconfirmed = 0;//将待确认分数置为0
        return true;
    }

    /**
    Migration Functions
    */
    /// @notice 合约拥有者获取榜单明细信息
    /// @param boardHash 榜单hash
    /// @return 返回榜单名称、榜单描述、榜单玩家数、榜单拥有者
    function migrationGetBoard(bytes32 boardHash) constant isOwner public returns(bytes32,string,uint,address) {
        return (boards[boardHash].boardName, boards[boardHash].boardDescription, boards[boardHash].numPlayers, boards[boardHash].boardOwner);
    }

    /// @notice 合约拥有者修改榜单信息
    /// @param boardHash 需要修改的榜单hash值
    /// @param name 榜单的新名称
    /// @param boardDescription 榜单的新描述
    /// @param numPlayers 榜单的新用户数
    /// @param boardOwner 榜单新的拥有者
    /// @return true
    function migrationSetBoard(bytes32 boardHash, bytes32 name, string boardDescription, uint8 numPlayers, address boardOwner) isOwner public returns(bool) {
        boards[boardHash].boardName = name;
        boards[boardHash].boardDescription = boardDescription;
        boards[boardHash].numPlayers = numPlayers;
        boards[boardHash].boardOwner = boardOwner;
        return true;
    }

    /// @notice 合同拥有者读取玩家数据
    /// @param boardHash 榜单hash
    /// @param playerID 玩家ID
    /// @return Player metadata
    function migrationGetPlayer(bytes32 boardHash, uint8 playerID) constant isOwner public returns (uint, bytes32, address, uint, uint, uint){
        Player storage p = boards[boardHash].players[playerID];
        return (playerID, p.playerName, p.playerAddress, p.score, p.score_unconfirmed, p.isActive);
    }

    /// @notice 合约拥有者重写玩家数据
    /// @param boardHash 榜单hash
    /// @param playerID 玩家ID
    /// @param playerName 玩家名称
    /// @param playerAddress 玩家地址
    /// @param score 玩家分数
    /// @param score_unconfirmed 未确认分数
    /// @param isActive 玩家状态
    /// @return true
    function migrationSetPlayer(bytes32 boardHash, uint playerID, bytes32 playerName, address playerAddress, uint score, uint score_unconfirmed, uint isActive) isOwner public returns (bool) {
        Board storage g = boards[boardHash];
        g.players[playerID] = Player(playerName, playerAddress, score, score_unconfirmed, isActive);
        return true;
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章