zoukankan      html  css  js  c++  java
  • 程序员的自我救赎---12.2.1: 虚拟币交易平台(区块链) 上【发行区块链代币】

    《前言》

    (一) Winner2.0 框架基础分析

    (二)PLSQL报表系统

    (三)SSO单点登录

    (四) 短信中心与消息中心

    (五)钱包系统

    (六)GPU支付中心

    (七)权限系统

    (八)监控系统

    (九)会员中心

    (十) APP版本控制系统

    (十一)Winner前端框架与RPC接口规范讲解

    (十二)上层应用案例

    (十三)总结

     《虚拟币交易平台(区块链)》 ——上 【发行区块链代币】

    关于对于区块链 或者 比特币还没有一个基础概念的就自行百度吧,什么“去中心化”,“挖矿”,“POW”,等等等等,乱七八糟的东西

    就随便找个三分钟科普视频看一下就行了。我们这边将就直接进入正题,如何发行一个区块代币。

    说到代币,始终绕不过比特币,关于比特币有几个点还是要说一下:

    1,首先比特币的代码是开源的,所以很多山寨币修改比特币加密算法然后就发行自己的代币,很多没有创新意义的山寨币是没有价值的。

    2,比特币是基于区块链技术的首个应用。 更准确的说在没有“区块链”这个名词之前把“BitCoin”代表比特币,而“bitCoin”代表实现比特币的技术(大小写不同)。 

    3,比特币的技术“区块链” 从最本质来说是一种思想,用什么语言去实现它是都可以的,哪怕是Java,C#。但常用的还是C/C++。 

    4,比特币不代表区块链,比特币的区块链技术有很多不足的地方,比如:不具有“图灵完备”。  就是他不能像Android一样,基于他开发应用。

    5,第一条里面说没有竞争优势的山寨币是没有价值的。这种就是通常用来做ICO的“空气币”,而所谓的ICO今天基本可以看做割韭菜的代名词现在国家已经禁止了ICO。

    而有竞争优势的才有价值比如扩容了区块的莱特币(LTC)。比如以“智能合约”著称的 以太坊(以太坊是区块链技术,代币是以太币,另外还有硬分叉的以太经典)。

    说了这么多废话,今天我们这一篇只要讨论的还是如何发币,直接来点干货?

    总的来说2种方式:

    A: 直接下载比特币的源代码,如何能看懂C/C++ 就没难度,直接改掉名字就行了。

    具体怎么搞呢?非常简单:https://github.com/bitcoin/bitcoin  直接上GITHub下载 然后改吧。

    B:前面说了比特币的区块链是不具备图灵完备的,所以有些将区块链封装成平台的我们只要基于这个平台

    开发自己的代币应用就行了。比如: 超级账本(hyperledger), 还有前面提到的以太坊(Ethereum)。

    下面我会重点讲一下以太坊。

    ===================================华丽的分割线==========================================

    讲到这里我们才是真的切入正题了,利用以太坊发币。关于以太坊是什么?什么是智能合约? 这些能百度到的概念性东西

    我就不做百度的搬运工了,我直接讲实操的部分。

    首先我们可以下载需要我们需要下载以太坊的钱包客户端:

    其实以太坊的钱包客户端 主要的都是Geth这个工具, Geth是一定要下载的 Ethereum Wallet 和 Mist 都是基于geth做了一个图形化界面。

    注意,重点来了!以太坊是基于go语言发开的,本身我们也可以下载以太坊的源码然后像改比特币源码一样去改成自己的区块链,

    Github的以太坊源码链接: https://github.com/ethereum/go-ethereum

    但是回归到本质是上来说还是那句话:首先要能看的懂,其次改完之后要有竞争力。

    我们贴一下钱包客户端的图,整篇博客都是文字 就太不利于阅读了,我这里贴的 Ethereum Wallet,推荐使用Ethereum Wallet

    默认以太坊钱包客户端会连接上真实以太坊“主链”也叫公有链,所以就在一直同步区块,这就很费事了,以太坊有专门开发人员提供测试环境也就是测试链(Rinkeby)

    这个我们晚点再说。

    如果利用以太坊发币第一种方式是,直接下载以太坊源码修改以太坊的话,那第二种办法就是利用以太坊的区块链技术发布一个私有链!

    怎么做?我们先尝试着发布一个自己的私有链。

    步骤如下:

    1,下载geth钱包客户端:

    2,下载图形化钱包客户端:

    3,自己创建一个私有链需要一个创世文件genesis.json,这里我直接提供了。(geth从1.7版本开始创世文件要加config参数)

    {
        "config": {
            "chainId": 15,
            "homesteadBlock": 0,
            "eip155Block": 0,
            "eip158Block": 0
        },
        "coinbase" "0x0000000000000000000000000000000000000000",
        "difficulty" "0x40000",
        "extraData" "",
        "gasLimit" "0xffffffff",
        "nonce" "0x0000000000000042",
        "mixhash" "0x0000000000000000000000000000000000000000000000000000000000000000",
        "parentHash" "0x0000000000000000000000000000000000000000000000000000000000000000",
        "timestamp" "0x00",
        "alloc": { }
    }

    将这个文件,保存到txt,改名叫genesis.json,当然名字可以自己决定。这就是一个普通的json文件。

    里面的字段是什么意思呢? 我来解释一下(其实也是百度查的):

    参数名称参数描述
    mixhash 与nonce配合用于挖矿,由上一个区块的一部分生成的hash。注意他和nonce的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件。
    nonce nonce就是一个64位随机数,用于挖矿,注意他和mixhash的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件。
    difficulty 设置当前区块的难度,如果难度过大,cpu挖矿就很难,这里设置较小难度
    alloc 用来预置账号以及账号的以太币数量,因为私有链挖矿比较容易,所以我们不需要预置有币的账号,需要的时候自己创建即可以。
    coinbase 矿工的账号,随便填
    timestamp 设置创世块的时间戳
    parentHash 上一个区块的hash值,因为是创世块,所以这个值是0
    extraData 附加信息,随便填,可以填你的个性信息
    gasLimit 该值设置对GAS的消耗总量限制,用来限制区块能包含的交易信息总和,因为我们是私有链,所以填最大。

      

    这里暂时不要去懂太多,反正知道 我们需要一个创世文件genesis.json即可。

    4,将创世文件保存下来,我这里放到C:Program FilesGeth, 我这里是因为Geth客户端 安装就在C盘,如果C盘空间不是很足就不建议安装在C盘了

    正式环境的主链现在有60G左右,很占空间。

    5,运行命令通过cmd命令运行。  直接打开geth就链接到主链了,默认是主链,所以我们通过命令加载创世文件。

     geth –datadir “%cd%chain” init genesis.json

    有可能会出现找不到文件的状况,所以我们要定位一下geth的目录。

     

     创世区块加载成功了!

    6, 再通过命令来启动私有链,并进入控制台:

    geth --identity "Nearetherum" --rpc --rpccorsdomain "*" --datadir “%cd%chain” --port "30303" --rpcapi "db,eth,net,web3" --networkid 11100   console

     

     私有链启动成功了!

     我们来看看geth的一些基本参数:

    参数描述
    identity:区块链的标示,随便填写,用于标示目前网络的名字
    init:指定创世块文件的位置,并创建初始块
    datadir:设置当前区块链网络数据存放的位置
    port:网络监听端口
    rpc:启动rpc通信,可以进行智能合约的部署和调试
    rpcapi:设置允许连接的rpc的客户端,一般为db,eth,net,web3
    networkid:设置当前区块链的网络ID,用于区分不同的网络,是一个数字
    console:启动命令行模式,可以在Geth中执行命令
    nodiscover:禁止被网络中其它节点发现,需要手动添加该节点到网络

    执行 personal命令,这个私有链里没有任何钱包

    我们创建一个钱包账户试试:

    personal.newAccount("123456")

     再执行personal:

    账户创建成功了。

    好了,到这里我们的私有链就创建的差不多了,接下来就是在我们的私有链里我们要运用起来,就得有人挖矿,挖矿其实就是记账。

    输入命令:miner.start() 或者miner.start(1)  。这里miner.start(1) 就是一个线程挖矿的意思。 这里停止挖矿是:miner.stop()

     挖矿就开始了。

     由于我们在挖矿也就是记账,所以区块会慢慢增加,占用的空间也会越来也多。

    注意:挖矿挖到的ether币会默认保在第一个账户中即eth.acccounts[0]中,所以要先创建一个钱包!

    7,geth 不要关让他继续挖矿,我们打开图形化界面看看,这时会发现右上角有一个PRIVATE-NET!

    PRIVATE-NET,意思就是启动链接的是私有链网络。 如果我们没有启动私有链默认是链接主链了。

    后面我们还会介绍以太坊的测试环境Rinkeby。

    我们直接点击运行,钱包客户端,这时候回发现钱包客户端里面有一个钱包地址,这就是我们刚刚在Geth里面创建的钱包。

     

     看看 是不是我们上面创建的钱包,这里我们里面就有私有链的以太坊了。这15个以太币就是我们刚刚挖矿得到了,我们继续开启挖矿,就可以继续获得以太币!

     不过这是私有链的。当然我们可以在私有链上部署智能合约!

    这里就比我们直接拿以太坊的源码去改方便太多了。直接用以太坊发布自己的私有链,发布自己的代币了!

    不过缺点是私有链我们需要别人来同步我们的区块链,并且还要有人来挖矿记账以及确认交易,如果停止挖矿的话,就不会再产生以太币,智能合约也没用了

    说白了就是没人挖矿就没有新的区块来产生。

    ===========================================华丽的分割线===================================

    我们来回顾一下以上我们讲了三种发行代币的方式:

    1,上GitHub上下载比特币的源码然后进行修改。缺点:需要懂C/C++,并且能充分理解比特币思想然后才能改动。

    并且比特币底层区块不具备图灵完备,从长远来说也就只能发个币。所以不推荐!

    2,以太坊同样是开源的,我们可以上Github下载以太坊的源码然后修改,优点是以太坊是可以编程的,但同样需要精通Go语言并且

    要去改底层算法,所以不推荐!

    3,利用以太坊技术发布一个私有链/联盟链。缺点是需要人挖矿记账。(这里说明一下比特币,莱特币都是需要人挖矿记账的,以太币也一样。)

    那么问题来了,有没有不需要挖矿的,比如那些做ICO的圈钱割韭菜的,他们就根本没有矿工,是怎么回事?

    接下来才是我整篇博客要写的重点?  基于“智能合约”发行代币!

    写了这么多才写到重点,上面写的那些都基本是废话了。还是那句话,我这里更多的就写实操的东西,关于概念性的玩意,我就不做百度

    的搬运工了。在此之前我们先记住几个网站:

    以太坊官网:https://www.ethereum.org/

    以太坊爱好者:http://ethfans.org/

    以太坊区块浏览器:https://etherscan.io/

    以太坊测试环境Rinkeby状态网:https://www.rinkeby.io/

    =======================================华丽的分割线==============================

    正文开始,首先我们打开以太坊钱包客户端,可能有的人会卡一下,没关系因为钱包客户端会在本地创建区块,并且开始同步一部分区块。

    打开之后我们看到第一个选择:

      稍微懂一点英文我们都能看明白什么意思,Main Network 就是主链,Test Network 就是测试链。Main Network也就是真实的以太坊环境,我们可以尝试进主链看看。不过主链现在区块应该有60多G,所以同步时间会比较久。

    而且以太币现在已经涨到了5千大洋一个,我们基于以太坊部署智能合约 发型代币是需要“以太币”的。 这对于我们新手来说,反复尝试部署,不可能去交易所买那么多以太币来测试。(可以尝试挖矿获得以太坊,但是太难!)

    所以,我们用测试环境“Rinkeby”。本身以太坊有几个测试链Rinkeby,只是其中之一!(比如还有:ropsten 但是不推荐使用了)

    我们创建一个在Rinkeby 测试链的以太坊钱包:

     

    正常情况下的话,这里创建完钱包之后就一步一步进入到钱包客户端主界面了,但是有可能会出现链接不上Rinkeby的故障

    比如我这种:

     这是什么原因呢?其实是因为我们本地没有加载Rinkeby的创始区块,并且客户端也不知道去哪里同步区块。这个时候我们就要用到我们

    前面提到的那些网站,我们打开:https://www.rinkeby.io/   访问速度比较慢的就下载个翻墙软件。

     这里我们看到首先我们需要Rinkeby的创始区块json,跟前面我们发私有链流程是一样的,把Rinkeby.json 保存在本地geth目录下。

    (由于之前的文章是我昨天用笔记本写的,今天我换了台式机所以我geth的目录变成了d盘下,没关系我们在cmd命令窗口里,定位一下目录就行了)

    然后流程是一样的,上面我们看到https://www.rinkeby.io/#geth 里面有四个节点选择,关于这四个节点要细讲起来,就要讲的东西比较多,这里我们就选第一个吧:Archive node 。

    geth --datadir=$HOME/.rinkeby init rinkeby.json

    然后使用同步全节点:

    geth --networkid=4 --datadir=$HOME/.rinkeby --cache=1024 --syncmode=full --ethstats='demon28@163.com' --bootnodes=enode://a24ac7c5484ef4ed0c5eb2d36620ba4e4aa13b8c84684e1b4aab0cebea2ae45cb4d375b77eab56516d34bfbd3c1a833fc51296ff084b770b94fb9028c4d25ccf@52.169.42.101:30303

    注意:--ethstats: 改成自己的邮箱,这个随便填个邮箱就行,但是要改一下!上面那个demon28@163.com 是我的邮箱,要改成自己的!

     

    这个时候再打开钱包客户端就可以连接Rinkeby了! 我们会发现rinkeby的区块要比以太坊主链要小好多,这里除了是测试环境以外还有一个原因,因为Rinkeby是不允许挖矿的。

     

     如果,Rinkeby的测试环境允许挖矿的话,那测试环境的区块就会越来越大,有可能测试环境甚至可能比主链区块都大,那么问题来了!

    前面我们说过通过部署智能合约发行代币,是需要以太币的,几遍是测试环境也是,那么Rinkeby又不能挖矿,我们怎么获得Rinkeby测试链下的以太币?

    其实很简单!直接问以太坊要呗,够直接吧! https://www.rinkeby.io/#faucet

    在此之前,我们得先有一个接受Rinkeby以太币的钱包地址,前面如果创建成功的,进入客户端界面之后会有钱包地址显示,如果没有的就创建一下!

     那么我们这里就创建了一个rinkeby钱包:

    也可以像前面我们操作私有链一样在geth里面操作,也是一样的。图形化界面只是封装了一下而已!

    接下来,我们继续来问官网要 rinkeby的测试以太币

    看到这个有点懵是不? 我也是,我也没明白上面要我填上面,起初我也以为只要填钱包地址就可以了,后来发现不是这么回事。

    百度一下,才读懂这句英文: Social network URL containing your Ethereum address ! (可能英文比较好的人没有我这个问题,我是属于英文比较不好的)

    意思就是说:"给他一个网络访问地址,打开里面的内容是rinkeby钱包地址!"   虽然意思明白了,但是具体这么搞呢?

    上面有说,用 “推脱” 或者 “谷歌+” 或者 “Facebook” 。 (都要翻墙,这个翻墙的问题就靠自己解决咯!)

    其实我上一次要币的时候用的github的gists,gists就相当于一个网络记事本,虽然也要翻墙,但是github还是比较熟悉的,不过现在github不让用了,所以我们今天我们尝试用一下google+

    关于谷歌账号怎么注册,怎么登陆这个我就不说了。登陆后,相当于我们发朋友圈,发QQ说说一样,发一条google+的消息就行了,如下图:

     然后把访问地址复制!

     再回到 https://www.rinkeby.io/#faucet然后直接要三个以太币。

     别要多了,本身这就是测试币要多了没用! 并且要的越多时间到账的时间越久。上面也写了,要3个的话到账时间要8个小时!。

    在这8个小时的等待时间里,我们可以让Rinkeby的区块慢慢同步。 一般睡一觉第二天就到了!

    经过漫长的等待,终于rinkeby的区块也同步完了,于此同时。我们索要的Rinkeby的测试币也到账了。

    有没有比较快的方式,也有! 直接加几个以太坊的群,问群里的大神要1个就好! 我们这里写的比较细,主要是把几个源头说清楚!

    我们长话短说,测试币到账之后,我们就要部署智能合约了!

    =====================================华丽的分割线==========================================

    智能合约 是用solidity 这种脚本语言开发的,类似JavaScript 不是很复杂。 我们点击钱包客户端的 右上角的 Contracts (合约)

    再选择部署智能合约!

     

    这里我直接提供一个官网的发行代币智能合约: 官网文档:https://ethereum.org/token

    pragma solidity ^0.4.16;
    
    interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }
    
    contract TokenERC20 {
        // Public variables of the token
        string public name;
        string public symbol;
        uint8 public decimals = 18;
        // 18 decimals is the strongly suggested default, avoid changing it
        uint256 public totalSupply;
    
        // This creates an array with all balances
        mapping (address => uint256) public balanceOf;
        mapping (address => mapping (address => uint256)) public allowance;
    
        // This generates a public event on the blockchain that will notify clients
        event Transfer(address indexed from, address indexed to, uint256 value);
    
        // This notifies clients about the amount burnt
        event Burn(address indexed from, uint256 value);
    
        /**
         * Constructor function
         *
         * Initializes contract with initial supply tokens to the creator of the contract
         */
        function TokenERC20(
            uint256 initialSupply,
            string tokenName,
            string tokenSymbol
        ) public {
            totalSupply = initialSupply * 10 ** uint256(decimals);  // Update total supply with the decimal amount
            balanceOf[msg.sender] = totalSupply;                // Give the creator all initial tokens
            name = tokenName;                                   // Set the name for display purposes
            symbol = tokenSymbol;                               // Set the symbol for display purposes
        }
    
        /**
         * Internal transfer, only can be called by this contract
         */
        function _transfer(address _from, address _to, uint _value) internal {
            // Prevent transfer to 0x0 address. Use burn() instead
            require(_to != 0x0);
            // Check if the sender has enough
            require(balanceOf[_from] >= _value);
            // Check for overflows
            require(balanceOf[_to] + _value > balanceOf[_to]);
            // Save this for an assertion in the future
            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);
            // Asserts are used to use static analysis to find bugs in your code. They should never fail
            assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
        }
    
        /**
         * Transfer tokens
         *
         * Send `_value` tokens to `_to` from your account
         *
         * @param _to The address of the recipient
         * @param _value the amount to send
         */
        function transfer(address _to, uint256 _value) public {
            _transfer(msg.sender, _to, _value);
        }
    
        /**
         * Transfer tokens from other address
         *
         * Send `_value` tokens to `_to` on behalf of `_from`
         *
         * @param _from The address of the sender
         * @param _to The address of the recipient
         * @param _value the amount to send
         */
        function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
            require(_value <= allowance[_from][msg.sender]);     // Check allowance
            allowance[_from][msg.sender] -= _value;
            _transfer(_from, _to, _value);
            return true;
        }
    
        /**
         * Set allowance for other address
         *
         * Allows `_spender` to spend no more than `_value` tokens on your behalf
         *
         * @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;
        }
    
        /**
         * Set allowance for other address and notify
         *
         * Allows `_spender` to spend no more than `_value` tokens on your behalf, and then ping the contract about it
         *
         * @param _spender The address authorized to spend
         * @param _value the max amount they can spend
         * @param _extraData some extra information to send to the approved contract
         */
        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;
            }
        }
    
        /**
         * Destroy tokens
         *
         * Remove `_value` tokens from the system irreversibly
         *
         * @param _value the amount of money to burn
         */
        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;
        }
    
        /**
         * Destroy tokens from other account
         *
         * 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;
        }
    }

    ====================================华丽的分割线=======================================

     如果没有rinkeby的测试以太币,这里是不能部署智能合约的,这里也牵扯到一个“燃料” 的知识点。燃料在以太坊里面叫“gas”。

    其实就是以太币,但是他的单位比以太币小很多很多。假如以太币是一元的话,gas大概就是0.000001分钱。当然这个随着以太坊的价格浮动的。

    我们接着来部署发行代币的智能合约:

    再来输入密码确认:

     

    回过头来点开自己的钱包,就可以看到部署的智能合约,也就是我们发行的区块链代币,在等待区块确认。 这里我们前面给的gas确认速度就会越快!

     

    以太坊是目前区块链中确认速度最快的,我们看到我们发行的Near币 已经成功了。

     我们尝试着新建几个账户相互之间用我们发行的NearCoin转账试试。

     

    每次交易都要收手续费的,这个手续费就是挖矿人员获得的收益之一。 

    到账了··!!

    另外,如果需要转账给别人,需要别人先添加观察你的智能合约,就可以获得了。

    ================================================华丽的分割线==============================================

    最后再回顾一下说的四种发行代币的方式:

    1,修改比特币源码。

    2,修改以太坊源码。

    3,发布私有链。

    4,部署智能合约 (重点)。

    行了,这篇博客写了我两天时间,中间有些概念我也不知道我理解的对不对。如果有理解错误的地方还望指正!

    在下一篇的博客中,我将根据我的经验来写如何开发一个 “虚拟币交易平台”。

    如果您觉得这篇文章对您有帮助,请捐助:

                                                                                                                      

    好了,就到这里有兴趣一起探讨Winner框架的可以加我们QQ群:261083244。或者扫描左侧二维码加群。

  • 相关阅读:
    Springboot开发微信公众号(四)
    Springboot开发微信公众号(三)
    springboot中Scheduled不执行的原因
    static方法里用@Autowire或者@Resource注入的属性
    Spring boot 读取配置文件application.yml (自定义属性值)
    Apache-Flink深度解析-SQL概览
    Apache-Flink深度解析-DataStream-Connectors之Kafka
    Apache-Flink深度解析-JOIN 算子
    Apache-Flink深度解析-TableAPI
    Spark streaming消费Kafka的正确姿势
  • 原文地址:https://www.cnblogs.com/demon28/p/8492339.html
Copyright © 2011-2022 走看看