以太訪+IPFS實現一個雲存儲系統(存儲就送幣)

以太訪+IPFS實現一個簡單的雲存儲系統(存儲就送幣)

作者: AlexTan

CSDN: http://blog.csdn.net/alextan_

Github: https://github.com/AlexTan-b-z

e-mail: [email protected]


一、介紹:

上一篇博文"以太坊+IPFS+WEB 電商平臺開發講解"介紹了用以太訪+IPFS實現電商平臺的思路、合約接口的實現以及一些相關的基本概念。這篇博文將講解具體的一個簡單的實戰項目,及用以太訪+IPFS實現的一個雲存儲,並用ERC20標準實現了自己的代幣,以太訪加IPFS實現存儲就送代幣。

  • 本系統用IPFS來充當存儲介質,及用戶所上傳的東西都是存在IPFS上的,IPFS會返回一個地址(什麼是地址?簡單理解,你可以通過地址來找到你所存儲文件的內容)。

  • 用合約來存儲用戶以及每個用戶所存儲的IPFS地址

  • 用合約實現一個ERC20標準的代幣。代幣名字叫CloudB,即雲幣,總發行量10000000。

  • 起初每存一次文件便會贈送10個雲幣,但會花費少量的gas費。

  • 待10000000個雲幣贈送完後,每存儲一個文件會花費2個雲幣以及少量的gas費。

  • 查看文件不會產生任何費用。

二、什麼是ERC20標準的代幣:

ERC20是以太坊定義的一個代幣標準。要求我們在實現代幣的時候必須要遵守的協議,如指定代幣名稱、總量、實現代幣交易函數等,只有支持了協議才能被以太坊錢包支持。其接口如下:

contract ERC20Interface {



    string public constant name = "Token Name";

    string public constant symbol = "SYM";

    uint8 public constant decimals = 18;  // 18 is the most common number of decimal places



    function totalSupply() public constant returns (uint);

    function balanceOf(address tokenOwner) public constant returns (uint balance);

    function allowance(address tokenOwner, address spender) public constant returns (uint remaining);

    function transfer(address to, uint tokens) public returns (bool success);

    function approve(address spender, uint tokens) public returns (bool success);

    function transferFrom(address from, address to, uint tokens) public returns (bool success);



    event Transfer(address indexed from, address indexed to, uint tokens);

    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);

}

簡單說明一下:

  • name : 代幣名稱;

  • symbol: 代幣符號;

  • decimals: 代幣小數點位數,代幣的最小單位, 18表示我們可以擁有 .0000000000000000001單位個代幣;

  • totalSupply() : 發行代幣總量;

  • balanceOf(): 查看對應賬號的代幣餘額;

  • transfer(): 實現代幣交易,用於給用戶發送代幣(從我們的賬戶裏);

  • transferFrom(): 實現代幣用戶之間的交易;

  • allowance(): 控制代幣的交易,如可交易賬號及資產;

  • approve(): 允許用戶可花費的代幣數;

代幣合約代碼(TokenERC20.sol):


pragma solidity ^0.4.16;

interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }

contract TokenERC20 {
    string public name;
    string public symbol;
    uint8 public decimals = 18;  // decimals 可以有的小數點個數,最小的代幣單位。18 是建議的默認值
    uint256 public totalSupply;

    // 用mapping保存每個地址對應的餘額
    mapping (address => uint256) public balanceOf;
    // 存儲對賬號的控制
    mapping (address => mapping (address => uint256)) public allowance;

    // 事件,用來通知客戶端交易發生
    event Transfer(address indexed from, address indexed to, uint256 value);

    // 事件,用來通知客戶端代幣被消費
    event Burn(address indexed from, uint256 value);

    /**
        * 初始化構造
        */
    function TokenERC20(uint256 initialSupply, string tokenName, string tokenSymbol, address rootWallet) public {
        totalSupply = initialSupply * 10 ** uint256(decimals);  // 供應的份額,份額跟最小的代幣單位有關,份額 = 幣數 * 10 ** decimals。
        balanceOf[rootWallet] = totalSupply;                // 指定賬戶地址擁有所有的代幣
        name = tokenName;                                   // 代幣名稱
        symbol = tokenSymbol;                               // 代幣符號
    }

    /**
        * 代幣交易轉移的內部實現
        */
    function _transfer(address _from, address _to, uint _value) internal {
        // 確保目標地址不爲0x0,因爲0x0地址代表銷燬
        require(_to != 0x0);
        // 檢查發送者餘額
        require(balanceOf[_from] >= _value);
        // 確保轉移爲正數個
        require(balanceOf[_to] + _value > balanceOf[_to]);

        // 以下用來檢查交易,
        uint previousBalances = balanceOf[_from] + balanceOf[_to];
        // Subtract from the sender
        balanceOf[_from] -= _value;
        // Add the same to the recipient
        balanceOf[_to] += _value;
        Transfer(_from, _to, _value);

        // 用assert來檢查代碼邏輯。
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }

    /**
        *  代幣交易轉移
        * 從自己(創建交易者)賬號發送`_value`個代幣到 `_to`賬號
        *
        * @param _to 接收者地址
        * @param _value 轉移數額
        */
    function transfer(address _to, uint256 _value) public {
        _transfer(msg.sender, _to, _value);
    }

    /**
        * 賬號之間代幣交易轉移
        * @param _from 發送者地址
        * @param _to 接收者地址
        * @param _value 轉移數額
        */
    function transferFrom(address _from, address _to, uint256 _value) public payable returns (bool success) {
        require(_value <= allowance[_from][msg.sender]);     // Check allowance
        allowance[_from][msg.sender] -= _value;
        _transfer(_from, _to, _value);
        return true;
    }

    /**
        * 設置某個地址(合約)可以創建交易者名義花費的代幣數。
        *
        * 允許發送者`_spender` 花費不多於 `_value` 個代幣
        *
        * @param _spender The address authorized to spend
        * @param _value the max amount they can spend
        */
    function approve(address _spender, uint256 _value) public
        returns (bool success) {
            allowance[msg.sender][_spender] = _value;
            return true;
        }

    /**
        * 設置允許一個地址(合約)以我(創建交易者)的名義可最多花費的代幣數。
        *
        * @param _spender 被授權的地址(合約)
        * @param _value 最大可花費代幣數
        * @param _extraData 發送給合約的附加數據
        */
    function approveAndCall(address _spender, uint256 _value, bytes _extraData)
        public
        returns (bool success) {
        tokenRecipient spender = tokenRecipient(_spender);
        if (approve(_spender, _value)) {
            // 通知合約
            spender.receiveApproval(msg.sender, _value, this, _extraData);
            return true;
        }
    }

    /**
        * 銷燬我(創建交易者)賬戶中指定個代幣
        */
    function burn(uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] >= _value);   // Check if the sender has enough
        balanceOf[msg.sender] -= _value;            // Subtract from the sender
        totalSupply -= _value;                      // Updates totalSupply
        Burn(msg.sender, _value);
        return true;
    }

    /**
        * 銷燬用戶賬戶中指定個代幣
        *
        * Remove `_value` tokens from the system irreversibly on behalf of `_from`.
        *
        * @param _from the address of the sender
        * @param _value the amount of money to burn
        */
    function burnFrom(address _from, uint256 _value) public returns (bool success) {
        require(balanceOf[_from] >= _value);                // Check if the targeted balance is enough
        require(_value <= allowance[_from][msg.sender]);    // Check allowance
        balanceOf[_from] -= _value;                         // Subtract from the targeted balance
        allowance[_from][msg.sender] -= _value;             // Subtract from the sender's allowance
        totalSupply -= _value;                              // Update totalSupply
        Burn(_from, _value);
        return true;
    }
}

三、存儲合約代碼實現

存儲合約裏實現了,upload上傳(實現了贈送代幣以及花費代幣)、download下載、獲取用戶所存儲文件的個數等函數。具體代碼如下:

SaveCloud.sol:


pragma solidity ^0.4.16;
import "./TokenERC20.sol";


contract SaveCloud {
    enum IsFinish {Yes, No}

    IsFinish isFinish; // 用於判斷代幣是否已經贈送完
    address private owner; // 合約的創建者
    uint public saveIndex; // 合約存儲文件的總數量
    mapping (address=>string[]) public ipfs; // 用於存放每個用戶所存儲到ipfs的ipfs地址 address => 用戶地址,string[] => ipfs地址
    TokenERC20 public token; // 代幣合約的實例化對象
    
    event Upload(string ipfsAddress );
    event Download(uint number);

    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    function SaveCloud() public {
    /* 構造函數,部署合約時便會調用這個函數 */
        owner = msg.sender;
        saveIndex = 0;
        isFinish = IsFinish.No;
    }

    function initToken(address tokenAddr) public onlyOwner { 
        /* 實例化代幣合約對象,只有創建合約者才能調用此函數 */
        token = TokenERC20(tokenAddr);
    }

    function upload(string ipfsAddress) public payable returns (bool success) {
        if (token.balanceOf(address(this)) >= 10 && isFinish == IsFinish.No) {
            token.transfer(msg.sender, 10); // 合約給sender轉代幣,存東西送代幣
        }else {
            isFinish = IsFinish.Yes;
        }

        if (isFinish == IsFinish.Yes) {
            token.transferFrom(msg.sender, owner, 2);
            // 需要aprove(授權),如果不加aprove的話,誰都可以來調這個函數花別人的代幣了,存東西,花代幣,代幣轉給合約賬戶
        }
        ipfs[msg.sender].push(ipfsAddress);
        saveIndex += 1;
        Upload(ipfsAddress);
        return true;
    }

    function download(uint number) external returns (string ipfsAddr) {
        /* 獲取用戶的所存放文件的位置(ipfs地址) */
        Download(number);
        return ipfs[msg.sender][number];
    }

    function getLength() external view returns (uint length) {
        /* 獲取用戶所存放文件的個數 */
        return ipfs[msg.sender].length;
    }
}

三、部署代碼:

筆者使用的是truffle框架,其本地部署代碼如下:

2_deploy_contracts.js:


var TokenERC20 = artifacts.require("./TokenERC20.sol");
var SaveCloud = artifacts.require("./SaveCloud.sol");

module.exports = function(deployer) {
  deployer.deploy(SaveCloud).then(function() {
    /* 實例化SaveCloud合約後實例化代幣合約 */
    return deployer.deploy(TokenERC20, 10000000, "cloudb", "CloudB", SaveCloud.address);
  })
};

四、js調用代碼:

js調用代碼、以及前端頁面代碼,後續將會發送到github,還請大家自行查看。

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