程序員的自我救贖---12.2.3: 虛擬幣交易平臺(區塊鏈) 下 【C#與以太坊通信】

 

《前言》html

(一) Winner2.0 框架基礎分析前端

(二)PLSQL報表系統node

(三)SSO單點登陸git

(四) 短信中心與消息中心github

(五)錢包系統web

(六)GPU支付中心編程

(七)權限系統json

(八)監控系統api

(九)會員中心瀏覽器

(十) 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。 參考:http://www.javashuo.com/article/p-hwiogiri-gu.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。

若是我這篇博客對您有幫助,請打賞:

 

相關文章
相關標籤/搜索