程序员的自我救赎---12.2.3: 虚拟币交易平台(区块链) 下 【C#与以太坊通讯】

 

《前言》

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

(二)PLSQL报表系统

(三)SSO单点登录

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

(五)钱包系统

(六)GPU支付中心

(七)权限系统

(八)监控系统

(九)会员中心

(十) APP版本控制系统

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

(十二)上层应用案例

(十三)总结

 

 虚拟币交易平台(区块链)下 【C#与以太坊通讯】

 

这一篇,也是区块链代币交易平台的最后一篇博客。所以内容都是基于前面两篇博客的,没有看过前面两篇的建议先过一遍。

 

    12.2.1 :虚拟币交易平台(区块链) 上 【发行区块链代币】

    12.2.2: 虚拟币交易平台(区块链) 中 【开发交易所】

 

说会到终点,我们还是接着代币交易平台的前提往前讲。前面我们讲述了如何基于以太坊发行区块链代币  以及 如何开发一个代币交易平台。

但是关键点是这代币和交易平台如何对接?

 

我们知道既然在交易平台交易,关于区块链代币的操作就至少有三种:

1,通过交易所创建用户代币钱包。 (注册)

2,客户从钱包客户端转入代币到交易所。(充值)

3,从交易所将代币转出到钱包客户端。 (提币)

 

其他的操作就是交易所(Web)做的事情,基本就不涉及和区块链之间的交互。今天我们还是以以太坊的测试环境Rinkeby为例。

我们先打开Geth客户端,加载Rinkeby的创始区块,并启动控制台。

 

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

 

 

 由于,我前面两篇博客是用另外一台电脑写的,那台电脑geth安装是在C盘。现在这台电脑C盘空间不是很够所以安装在D盘,这里我们只要cd定位一下就行。

 再来我们启动以太坊的控制台Console。

 

geth --networkid=4 --datadir=$HOME/.rinkeby --syncmode=light  --bootnodes=enode://a24ac7c5484ef4ed0c5eb2d36620ba4e4aa13b8c84684e1b4aab0cebea2ae45cb4d375b77eab56516d34bfbd3c1a833fc51296ff084b770b94fb9028c4d25ccf@52.169.42.101:30303 --identity "NearEth" --rpc --rpccorsdomain "*" --rpcapi "db,eth,net,web3,personal" console

 

这里跟【发行区块链代币】那篇文章中的“同步区块”命令略有一点不同。

 

首先,我用的是Light node(轻节点),轻节点相比Archive node(归档节点) 去掉了历史日志。所以同步区块的速度会快很多,前面的操作如何一直用的是归档节点就继续用归档节点。https://www.rinkeby.io/#geth

其次,我加入了--rpc ,--rpccorsdomain , --rpcapi 。这个是设置允许通过RCP接口访问,设置可以有哪些访问权限。这里还可以设置访问的端口,没设置的话默认是:8545。 参考:https://www.cnblogs.com/tinyxiong/p/7918706.html

最后,console命令。进入控制台。

 

 

 

下面我们做四个最基本的命令,顺便也回顾一下geth的操作命令(如何不知道有哪些命令的话就直接打eth 或者 personal 看他的API文档):

1,创建钱包:personal.newAccount('123456')                                                                                                 //参数为钱包客户端密码

2,查询余额:eth.getBalance("0x820858f59bc885dcc088349e0ed8dcab6bfbf948")                                      //参数为创建的客户端钱包地址

3,解锁钱包:personal.unlockAccount("0x820858f59bc885dcc088349e0ed8dcab6bfbf948","123456")         //参数1:钱包地址;参数2:钱包密码。  钱包默认状态是加锁了。要转账的话需要先解锁。

4,钱包转账:eth.sendTransaction({from:"0x820858f59bc885dcc088349e0ed8dcab6bfbf948",to:"0xcbe33208f86166a1c1cb52a3105a0a36b20c35a5",value:web3.toWei(0.3,"ether")})       //参数:json数据:from ,to,value

 

(这里要说明以太币有很多单位 可参考:https://www.jianshu.com/p/b56552b1d1a0 )

 

 

 

 

 

 

创建钱包,查询余额,解锁钱包 都是立马可以看到的,转账需要等待12个区块确认后才能成功,转账操作完成之后会给一个区块地址,这个区块地址可以直接上区块浏览器去查询交易。

 

https://www.rinkeby.io/#explorer

 

 

 

 

这里四个基本的操作要多尝试几次熟练一下!

 

 

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

 

下面我用C# 来做以上4个命令的操作,其实也挺简单的。以太坊是专门有提供各个编程语言的解决方案,用的最多的应该是JavaScript。这里感兴趣的自行去百度一下,我们重点说C#。

先来我们看到以太坊的官网:http://www.ethdocs.org/en/latest/connecting-to-clients/nethereum/index.html   。

官网是直接有提供SDK的,而且代码是托管在GitHub的,我可以直接访问Github看到Nethereum: https://github.com/Nethereum/Nethereum

 

直接用NuGet去添加程序集:

PM > Install-Package Nethereum.Web3


接下来我们创建一个Winform程序,然后添加Nethereum。

 


 

调用代码还是比较简单的,我直接贴一下代码,一会将项目github。

 

 

using Nethereum.Hex.HexTypes;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace EthClient
{
    public partial class Form1 : Form
    {

        static string rpcUrl = "http://127.0.0.1:8545";

        Nethereum.Web3.Web3 web3 = new Nethereum.Web3.Web3(rpcUrl);
     
        public Form1()
        {
            InitializeComponent();
        }


        private async void btn_queryAllAccount_Click(object sender, EventArgs e)
        {
            
            var accounts = await web3.Eth.Accounts.SendRequestAsync();
            var acclist = accounts.ToList();
            for (int i = 0; i < acclist.Count; i++)
            {
                this.tb_logList.Text += "账户" + i + ":" + acclist[i] + "\r\n";
            }
        }

        private async void btn_newAccount_Click(object sender, EventArgs e)
        {
            string pwd = this.tb_Pwd.Text.Trim();
            if (string.IsNullOrEmpty(pwd))
            {
                MessageBox.Show("密码不能为空");
                return;
            }
            var createAccount = await web3.Personal.NewAccount.SendRequestAsync(pwd);
            this.tb_logList.Text =  createAccount;
        }

        private async void btn_BlanceOf_Click(object sender, EventArgs e)
        {
            string accountAddress = this.tb_address.Text.Trim();
            if (string.IsNullOrEmpty(accountAddress))
            {
                MessageBox.Show("不能为空");
                return;
            }

            var value= await web3.Eth.GetBalance.SendRequestAsync(accountAddress);
            this.tb_logList.Text = value.Value.ToString();
        }

        private async void btn_unlock_Click(object sender, EventArgs e)
        {
            string accountAddress = this.tb_lockAccount.Text.Trim();
            string unLockPwd = this.tb_unlockPwd.Text;

            if (string.IsNullOrEmpty(accountAddress) || string.IsNullOrEmpty(unLockPwd))
            {
                MessageBox.Show("不能为空");
                return;
            }

            try
            {
                var res = await web3.Personal.UnlockAccount.SendRequestAsync(accountAddress, unLockPwd, 1500);
                this.tb_logList.Text = res.ToString();
            }
            catch (Exception ex)
            {
                this.tb_logList.Text ="密码错误!";
            }
        }

        private async void btn_sendTransfer_Click(object sender, EventArgs e)
        {
            string AccountFrom = this.tb_from.Text.Trim();
            string AccountTo = this.tb_to.Text;


            if (string.IsNullOrEmpty(AccountFrom) || string.IsNullOrEmpty(AccountTo))
            {
                MessageBox.Show("不能为空");
                return;
            }

           string AccountPwd=  InputBox.ShowInputBox("请输入密码!");

            bool unlockres = false;
            try
            {
                unlockres = await web3.Personal.UnlockAccount.SendRequestAsync(AccountFrom, AccountPwd, 1500);
                
            }
            catch (Exception ex)
            {
                this.tb_logList.Text = "钱包密码错误!";
                return;
            }
               
            if (!unlockres)
            {
                this.tb_logList.Text = "钱包密码错误!";
                return;
            }

           

            Nethereum.RPC.Eth.DTOs.TransactionInput input = new Nethereum.RPC.Eth.DTOs.TransactionInput();
            input.From = AccountFrom;
            input.To = AccountTo;

            HexBigInteger gas = new HexBigInteger(2100000);   //设置转账小号的gas
            input.Gas = gas;

            HexBigInteger price = new HexBigInteger(13000000000000000); //单位:wei
            input.Value =  price;

            var Block = await web3.Eth.Transactions.SendTransaction.SendRequestAsync(input);

            this.tb_logList.Text = Block;

        }
    }
}
View Code

 

 

 

 

 

 

 

 

这里我直接提供一下GitHub下载地址: https://github.com/demon28/Nethereum   

 

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

 

以上讲述了如何通过Nethereum的SDK让C# 这门语言去操作geth。 那么接下来问题来了,上面的操作是操作ETH的,也就是以太币。

那么我们自己发现的代币如何操作?

 

首先,如果对于使用以太坊发行区块链代币不清楚如何操作的可以先看一遍 【发行区块链代币】 。

 

接下来,我们基于我们自己发行的一个NearCoin来进行操作。

 

我们来学习一个新知识“ABI”,什么是ABI?发行代币也就是部署一个智能合约, 简单来说ABI就是智能合约的API接口。我们可以在这里找他到:

 

 

 

 

 

 

有了它就可以和自己发行的代币通讯了。这里不啰嗦直接贴代码,一目了然:

 

 

using Nethereum.Hex.HexTypes;
using Nethereum.Web3;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace NearCoinClient
{
    public partial class Form1 : Form
    {

        readonly static string rpcUrl = "http://127.0.0.1:8545";

        readonly static string abi = @"[ { ""constant"": true, ""inputs"": [], ""name"": ""name"", ""outputs"": [ { ""name"": """", ""type"": ""string"", ""value"": ""NearCoin"" } ], ""payable"": false, ""stateMutability"": ""view"", ""type"": ""function"" }, { ""constant"": false, ""inputs"": [ { ""name"": ""_spender"", ""type"": ""address"" }, { ""name"": ""_value"", ""type"": ""uint256"" } ], ""name"": ""approve"", ""outputs"": [ { ""name"": ""success"", ""type"": ""bool"" } ], ""payable"": false, ""stateMutability"": ""nonpayable"", ""type"": ""function"" }, { ""constant"": true, ""inputs"": [], ""name"": ""totalSupply"", ""outputs"": [ { ""name"": """", ""type"": ""uint256"", ""value"": ""100000000000000000"" } ], ""payable"": false, ""stateMutability"": ""view"", ""type"": ""function"" }, { ""constant"": false, ""inputs"": [ { ""name"": ""_from"", ""type"": ""address"" }, { ""name"": ""_to"", ""type"": ""address"" }, { ""name"": ""_value"", ""type"": ""uint256"" } ], ""name"": ""transferFrom"", ""outputs"": [ { ""name"": ""success"", ""type"": ""bool"" } ], ""payable"": false, ""stateMutability"": ""nonpayable"", ""type"": ""function"" }, { ""constant"": true, ""inputs"": [], ""name"": ""decimals"", ""outputs"": [ { ""name"": """", ""type"": ""uint8"", ""value"": ""10"" } ], ""payable"": false, ""stateMutability"": ""view"", ""type"": ""function"" }, { ""constant"": false, ""inputs"": [ { ""name"": ""_value"", ""type"": ""uint256"" } ], ""name"": ""burn"", ""outputs"": [ { ""name"": ""success"", ""type"": ""bool"" } ], ""payable"": false, ""stateMutability"": ""nonpayable"", ""type"": ""function"" }, { ""constant"": true, ""inputs"": [ { ""name"": """", ""type"": ""address"" } ], ""name"": ""balanceOf"", ""outputs"": [ { ""name"": """", ""type"": ""uint256"", ""value"": ""0"" } ], ""payable"": false, ""stateMutability"": ""view"", ""type"": ""function"" }, { ""constant"": false, ""inputs"": [ { ""name"": ""_from"", ""type"": ""address"" }, { ""name"": ""_value"", ""type"": ""uint256"" } ], ""name"": ""burnFrom"", ""outputs"": [ { ""name"": ""success"", ""type"": ""bool"" } ], ""payable"": false, ""stateMutability"": ""nonpayable"", ""type"": ""function"" }, { ""constant"": true, ""inputs"": [], ""name"": ""symbol"", ""outputs"": [ { ""name"": """", ""type"": ""string"", ""value"": ""NC"" } ], ""payable"": false, ""stateMutability"": ""view"", ""type"": ""function"" }, { ""constant"": false, ""inputs"": [ { ""name"": ""_to"", ""type"": ""address"" }, { ""name"": ""_value"", ""type"": ""uint256"" } ], ""name"": ""transfer"", ""outputs"": [], ""payable"": false, ""stateMutability"": ""nonpayable"", ""type"": ""function"" }, { ""constant"": false, ""inputs"": [ { ""name"": ""_spender"", ""type"": ""address"" }, { ""name"": ""_value"", ""type"": ""uint256"" }, { ""name"": ""_extraData"", ""type"": ""bytes"" } ], ""name"": ""approveAndCall"", ""outputs"": [ { ""name"": ""success"", ""type"": ""bool"" } ], ""payable"": false, ""stateMutability"": ""nonpayable"", ""type"": ""function"" }, { ""constant"": true, ""inputs"": [ { ""name"": """", ""type"": ""address"" }, { ""name"": """", ""type"": ""address"" } ], ""name"": ""allowance"", ""outputs"": [ { ""name"": """", ""type"": ""uint256"", ""value"": ""0"" } ], ""payable"": false, ""stateMutability"": ""view"", ""type"": ""function"" }, { ""inputs"": [ { ""name"": ""initialSupply"", ""type"": ""uint256"", ""index"": 0, ""typeShort"": ""uint"", ""bits"": ""256"", ""displayName"": ""initial Supply"", ""template"": ""elements_input_uint"", ""value"": ""10000000"" }, { ""name"": ""tokenName"", ""type"": ""string"", ""index"": 1, ""typeShort"": ""string"", ""bits"": """", ""displayName"": ""token Name"", ""template"": ""elements_input_string"", ""value"": ""NearCoin"" }, { ""name"": ""tokenSymbol"", ""type"": ""string"", ""index"": 2, ""typeShort"": ""string"", ""bits"": """", ""displayName"": ""token Symbol"", ""template"": ""elements_input_string"", ""value"": ""NC"" } ], ""payable"": false, ""stateMutability"": ""nonpayable"", ""type"": ""constructor"" }, { ""anonymous"": false, ""inputs"": [ { ""indexed"": true, ""name"": ""from"", ""type"": ""address"" }, { ""indexed"": true, ""name"": ""to"", ""type"": ""address"" }, { ""indexed"": false, ""name"": ""value"", ""type"": ""uint256"" } ], ""name"": ""Transfer"", ""type"": ""event"" }, { ""anonymous"": false, ""inputs"": [ { ""indexed"": true, ""name"": ""from"", ""type"": ""address"" }, { ""indexed"": false, ""name"": ""value"", ""type"": ""uint256"" } ], ""name"": ""Burn"", ""type"": ""event"" } ]";
        readonly static string contractAddress = "0xB03Aa55003C1a9C235A11B244e437Cbf062fB998";   //智能合约地址

        Nethereum.Web3.Web3 web3 = new Nethereum.Web3.Web3(rpcUrl);

        Nethereum.Contracts.Contract NearCoinContract;

      

        public Form1()
        {
            InitializeComponent();


            NearCoinContract = web3.Eth.GetContract(abi, contractAddress);

            web3.TransactionManager.DefaultGas = 210000;  //设置默认消耗的gas
        }

        private async void btn_queryAmount_Click(object sender, EventArgs e)
        {
            string accountAddress = this.tb_account.Text.Trim();
            if (string.IsNullOrEmpty(accountAddress))
            {
                MessageBox.Show("不能为空");
                return;
            }

            var getBalance = NearCoinContract.GetFunction("balanceOf");  //方法名为智能合约中的方法名

            var amount = await  getBalance.CallAsync<Int64>(accountAddress);   //注意单位Gwei

            this.tb_loglist.Text = amount.ToString();
        }

        private async void btn_send_Click(object sender, EventArgs e)
        {
            string acc1 = this.tb_from.Text.Trim();
            string acc2 = this.tb_to.Text.Trim();
            int amount =int.Parse(this.tb_amount.Text.Trim());
            string pwd = this.tb_pwd.Text.Trim();

            if (string.IsNullOrEmpty(acc1)|| string.IsNullOrEmpty(acc2) || amount<0 || string.IsNullOrEmpty(pwd) )
            {
                MessageBox.Show("不能为空或金额不能为0");
                return;
            }


            bool unlockAccountResult;
            try
            {
                 unlockAccountResult = await web3.Personal.UnlockAccount.SendRequestAsync(acc1, pwd, 150);
            }
            catch (Exception)
            {
                this.tb_loglist.Text = "账户解锁失败!";
                return;
            }


            if (!unlockAccountResult) 
            {
                this.tb_loglist.Text = "账户解锁失败,或密码错误!";
                return;
            }
         

            var transfer = NearCoinContract.GetFunction("transfer");
            var value = await transfer.SendTransactionAsync(acc1, acc2, Convert.ToString(amount,16)); //金额为16进制


            this.tb_loglist.Text = value.ToString();

        }
    }
}
View Code

 

 

 这里有个问题需要注意的是,关于数值接收,看下图:

 

 

 

如果智能合约中小数点太长,C#没有uint256 这么大的类型,尤其是官网默认的智能合约是18位数字,需要修改。

 

这里我们修改一下官网智能合约的小数点长度,然后重新部署就可以了。

 

 

界面图:

 

 

代码已经提交到Github了, 可以直接下载下来。地址:https://github.com/demon28/Nethereum

 区块链也好,以太坊也好都是一种新兴的技术,如果有哪里写的不对,或者是我理解错误的地方还望指正!

 

 

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

 

连续写了三篇博客,我想做一个总结:

 

在上一篇文章“开发交易所”中,我也写了很多。现在我们的国家政策对于比特币包括所有区块链代币都是打压的,对区块链技术提倡深入研究。

但是到今天为止,我们能看见区块链应用成功的项目也就只有“比特币”以及他的变种币。虽然说区块链技术诸多特性对未来技术性方向有着绝对的影响,但是区块链不是唯一的,或者说全面性替代现有技术的。

 

区块链,只是技术方向的选择之一,如同我们从互联网时代,到移动互联网时代。电脑始终存在,不会说智能手机全面替代台式机、笔记本。

 

另外,技术始终是技术。既然是技术那就让技术员去搞,现在互联网上天天在追捧什么“三点钟社群”,每天讨论区块链。有时候我都在怀疑是不是一堆庄家在造势,利用“技术概念” 让人觉得对知识缺乏,造成焦虑、甚至恐慌。

当人恐慌的时候就会不自觉的想,我是不是也得每天看 某某某 的微博来更新知识? 我是不是该买谁谁谁的书籍来提高自己的储备? 我是不是得去买两个比特币体验一下?

 

这都是现代人的焦虑,现在这样一个信息爆炸的年代,其实哪里跟的过来。 这些年听太多了:“云计算”、“物联网”、“O2o”,“共享经济”,“分享经济”。 

 

其实,完全不用那么担心,真正能改变世界的一定是简单易用,让生活更方便的。

 

 

 最后。区块链这么火,弄得我也冒出一种想法。能不能基于C# 写一个区块链? 我知道有一些区块链是可以基于C# 这门语言来部署智能合约,但是底层不是C#的。

可能是我对C# 有种归属感吧,这两年移动互联网的时代,C# 这门语言也没发力。身边好多人都转Java了,总感觉C#令人唏嘘!

 

当然,以我现在的知识储备来讲,搞个区块链还远远不可能的事。 

 有园友在我上一篇博客中留言说了一下NEO,我看了一下就是C#写的区块链,看的我心一凉,哈哈。

 

 好了,关于区块链代币交易所三篇文章就写完了。其实这里我只是侧重于开发交易平台,但是以太坊提出一个新名词DApp,有兴趣的去琢磨琢磨吧!

(其实可以不用下载钱包客户端,直接上Remix IDE  部署智能合约就行!)

 

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

 

 

 

有兴趣一起探讨Winner框架的可以加我们QQ群:261083244。

如果我这篇博客对您有帮助,请打赏:

 

 

 

 

 



 

posted @ 2018-03-11 16:35  Near_wen  阅读(2053)  评论(7编辑  收藏  举报