• neo-thinsdk-cs 之 thinWallet 接入私链


    2017年底刚开始接触区块链,目前在被 NEO 折磨。
    一开始被官方文档 NEO-GUI 搞得体无完肤(尤其是传说中的 F12),也找了各种调试工具用来搞 NeoContract,然并卵。
    直到有一天发现了 NewEconoLab ,在它的 github 上有一个项目 neo-thinsdk-cs ,用 C# 实现了一个轻钱包 —— thinWallet
    这个轻钱包只做了一件事情,就是做交易,它把整个交易的流程都体现出来了,给开发者展示了明确的可视化交易拼接流程(包括Attribute、Inputs、Outputs、Script、Witness等,我曾经用 VS F11 一行一行的调试跟踪过它的源代码,所有的这些交易必须的数据都组装好,放到一个数据结构里,非常清晰)。
    最重要的是分解了 UTXO 和 使得 Witness 变得可处理,以达成 NEO-GUI 达不到的操作。
    此外,目前的 thinWallet ,默认接入到 Testnet,还不支持直接接入 Mainnet ,也不支持接入到 NEO 私有链 ,我们都知道 Testnet 上的 Gas 十分珍贵,需要去申请才能有,而我们部署合约,动不动就消耗 990 Gas 。而在自己的私有链上 Gas 就是大白菜了,随便乱玩都可以。
    所以为了方便部署合约调试,今天的任务是将 thinWallet 接入到私有链中。

    1、环境准备

    • 基础要求:四台 Debian 9(其他发行版随你喜欢,单击此处查看支持的机器,建议创建一台虚拟机配好环境然后直接克隆,别忘了强制刷新 MAC 地址,要不然 IP 冲突) ,一台 Windows 10 ,.net core sdk,visual studio 2017 community,.net framework 4.7 SDK,NEO-CLI,搭私链跑共识,建钱包,拿到一亿的 NEO 币和至少 1000 个 Gas (相当于你的区块高度超过 125,这至少需要耗费31分钟),这些东西文档里都有,此处不再赘述。
    • 克隆项目NeoBlock-Mongo-StorageNEO_Block_APIneo-thinsdk-cs,三个项目都是直接用 master 分支就可以了。

    这里解释一下克隆下来的三个项目是什么样的关系,三个都是 .net 项目

    1. neo-thinsdk-cs 是一整个解决方案,用 vs 2017 打开,里面有一个 WPF 项目是 thinWallet ,把它设置为启动项目,直接 CTRL+F5 就可以运行了,当然默认是接入 Testnet 。项目里面有一个《NEO智能合约开发(一)不可能完成的任务.docx》,好好看看。运行结果如下:
      thinWallet-index
      这里我们按第二个按钮 ThinWallet Test 就好了,进去是这样的界面:
      thinWallet-test
    2. 我们可以看到右上角在调用一个 API for UTXO,这是一个 webapi ,由 NEO_Block_API 提供,大家可以看到这地方默认就是 Testnet ,区块高度也是 Testnet 的高度,切换网络那个按钮还没写功能。总共五个大面板都很好理解,第一个登录方式,第二个 api 信息,第三个账户信息(你的 utxo 将会出现在这里),第四个交易三要素,第五个决定是什么类型的交易。
    3. NEO_Block_API 里面用到了 mongodb ,所以我们的第三个项目 NeoBlock-Mongo-Storage 作为一个调度任务跑着,用来实时收集链上的交易信息存入 mongodb 中,供 NEO_Block_API 使用。
    4. 顺便说明一下, vs 2017 很人性化,你直接 CTRL+F5 或者生成解决方案 ,会帮你还原依赖库的,看见黄色感叹号不要慌。如果你无法打开项目或者无法还原或者出现任何异常,相信我,一定是你的打开方式不对,或者你改了里面的东西改错了,或者环境有问题,三个项目是绝对没有错误的,我验证了好几遍了,请回头好好检查自己的环境等基础设施吧。

    配置 NEO_Block_API

    到这里,基础环境全部搭建完毕,thinWallet 也可以在 Testnet 上正常使用了,那么接下来就需要开始接入私链了。首先我们要把 NEO_Block_API 的源代码增改一下,让它支持接入私链。
    我们先看一下源代码长什么样,还是 vs 2017 打开:
    neo-block-api-code-structure
    我已经生成过解决方案了,所以依赖库都没问题了,结构非常简单。Controllers 是控制器,就是一些 web 接口在这里实现,lib 就是要用到的工具,RPC 定义了一些数据结构,再加一个 Startup.cs 和 Program.cs 文件,典型的 .net core web 项目。
    我们可以看到里边有一个 mongodbsettings.json 文件带了个黄色警告标志,说明这个文件丢失了,因为在 .gitignore 里边把它写进去了,所以 git 就把它忽略了,问题不大,我们删掉它重新建立一个一样名字的文件就好了,我们可以找一下在哪里用到了这个文件,你可以 CTRL+SHIFT+F 全局查找,最终发现在 mongoHelper.cs 里面有这么一段:

    public mongoHelper() {
        var config = new ConfigurationBuilder()
            .AddInMemoryCollection()    //将配置文件的数据加载到内存中
            .SetBasePath(System.IO.Directory.GetCurrentDirectory())   //指定配置文件所在的目录
            .AddJsonFile("mongodbsettings.json", optional: true, reloadOnChange: true)  //指定加载的配置文件
            .Build();    //编译成对象  
        mongodbConnStr_testnet = config["mongodbConnStr_testnet"];
        mongodbDatabase_testnet = config["mongodbDatabase_testnet"];
        neoCliJsonRPCUrl_testnet = config["neoCliJsonRPCUrl_testnet"];
    
        mongodbConnStr_mainnet = config["mongodbConnStr_mainnet"];
        mongodbDatabase_mainnet = config["mongodbDatabase_mainnet"];
        neoCliJsonRPCUrl_mainnet = config["neoCliJsonRPCUrl_mainnet"];
    
        mongodbConnStr_NeonOnline = config["mongodbConnStr_NeonOnline"];
        mongodbDatabase_NeonOnline = config["mongodbDatabase_NeonOnline"];
    }
    

    那么事情就简单了,我们看到这里面一共有 8 个字段要用到,给它就是了,我们在新建的 mongodbsettings.json 文件写上这些内容:

    {
      "mongodbConnStr_testnet": "mongodb://127.0.0.1:27017",
      "mongodbDatabase_testnet": "testnet",
      "neoCliJsonRPCUrl_testnet": "http://47.96.168.8:20332",
    
      "mongodbConnStr_mainnet": "mongodb://127.0.0.1:27017",
      "mongodbDatabase_mainnet": "mainnet",
      "neoCliJsonRPCUrl_mainnet": "",
    
      "mongodbConnStr_privatenet": "mongodb://127.0.0.1:27017",
      "mongodbDatabase_privatenet": "privatenet",
      "neoCliJsonRPCUrl_privatenet": "http://192.168.1.135:20332",
    
      "mongodbConnStr_NeonOnline": "mongodb://127.0.0.1:27017",
      "mongodbDatabase_NeonOnline": "neononline"
    }
    

    mainnet 一时半会儿肯定用不着,我懒得写了,testnet 还是保留原样,加一个 privatenet 。注意 neoCliJsonRPCUrl_privatenet 这个字段的值是 http://192.168.1.135:20332 ,192.168.1.135 这台机器是四台共识机中的一台,开了 RPC ,就是:dotnet neo-cli.dll /rpc 。我们可以用 postman 来测试一下,随便找个接口,路由是: http://192.168.1.135:20332 就用 getbestblockhash 吧:
    先看一下我跑的四个共识节点,我的共识跑了挺久了,现在高度接近 7000 :
    consensus
    接下来我们看一下 rpc 接口调用结果:
    rpc-test
    如果你无法得到类似的结果,那还是得去多看看文档,跑跑共识。现在我们得到了正确结果,说明私链的 RPC 完全没有问题。此刻我们得到了第一个需要的接口就是刚才打开的 thinWallet 右上角 RPC Node 所需要的接口地址:http://192.168.1.135:20332
    mongodb 你得装一下(随你 linux 还是 windows,推荐 linux),为了演示方便,我装在了 windows 里面。装完之后就是 C:Program FilesMongoDBServer3.6in 里面有一堆东西。其中 mongod.exe 这个是服务端,因为国际惯例有个 d 。别急着双击它,你得先建立两个文件夹:C:datadb,不然双击就闪退了,其实你可以用 cmd 来打开,可以看到异常信息。现在你可以双击它了,结果就是一个控制台,打印了一堆日志,你可以用 mongodb 自带的可视化客户端连接一下。这边注意,我们不需要事先创建数据库。
    下面开始创建 webapi 接口,也就是新建一个控制器,在项目的 Controllers 文件夹下,新建一个类,取名:PrivatenetController.cs ,然后把随便 TestnetController.cs 或者 MainnetController.cs 里面的内容全部复制粘贴到我们的 PrivatenetController.cs 中,改一下两个地方,变成 privatenet :

    [Route("api/[controller]")]
    public class PrivatenetController : Controller
    {
        Api api = new Api("privatenet");
    

    接着 mongoHelper.cs 里面改一下,把私链的东西加进去:

    public string mongodbConnStr_testnet = string.Empty;
    public string mongodbDatabase_testnet = string.Empty;
    public string neoCliJsonRPCUrl_testnet = string.Empty;
    
    public string mongodbConnStr_mainnet = string.Empty;
    public string mongodbDatabase_mainnet = string.Empty;
    public string neoCliJsonRPCUrl_mainnet = string.Empty;
    
    public string mongodbConnStr_privatenet = string.Empty;
    public string mongodbDatabase_privatenet = string.Empty;
    public string neoCliJsonRPCUrl_privatenet = string.Empty;
    
    public string mongodbConnStr_NeonOnline = string.Empty;
    public string mongodbDatabase_NeonOnline = string.Empty;
    
    public mongoHelper() {
        var config = new ConfigurationBuilder()
            .AddInMemoryCollection()    //将配置文件的数据加载到内存中
            .SetBasePath(System.IO.Directory.GetCurrentDirectory())   //指定配置文件所在的目录
            .AddJsonFile("mongodbsettings.json", optional: true, reloadOnChange: true)  //指定加载的配置文件
            .Build();    //编译成对象  
        mongodbConnStr_testnet = config["mongodbConnStr_testnet"];
        mongodbDatabase_testnet = config["mongodbDatabase_testnet"];
        neoCliJsonRPCUrl_testnet = config["neoCliJsonRPCUrl_testnet"];
    
        mongodbConnStr_mainnet = config["mongodbConnStr_mainnet"];
        mongodbDatabase_mainnet = config["mongodbDatabase_mainnet"];
        neoCliJsonRPCUrl_mainnet = config["neoCliJsonRPCUrl_mainnet"];
    
        mongodbConnStr_privatenet = config["mongodbConnStr_privatenet"];
        mongodbDatabase_privatenet = config["mongodbDatabase_privatenet"];
        neoCliJsonRPCUrl_privatenet = config["neoCliJsonRPCUrl_privatenet"];
    
        mongodbConnStr_NeonOnline = config["mongodbConnStr_NeonOnline"];
        mongodbDatabase_NeonOnline = config["mongodbDatabase_NeonOnline"];
    }
    

    最后,Api.cs 里面加私链的东西:

    public Api(string node) {
        netnode = node;
        switch (netnode) {
            case "testnet":
                mongodbConnStr = mh.mongodbConnStr_testnet;
                mongodbDatabase = mh.mongodbDatabase_testnet;
                neoCliJsonRPCUrl = mh.neoCliJsonRPCUrl_testnet;
                break;
            case "mainnet":
                mongodbConnStr = mh.mongodbConnStr_mainnet;
                mongodbDatabase = mh.mongodbDatabase_mainnet;
                neoCliJsonRPCUrl = mh.neoCliJsonRPCUrl_mainnet;
                break;
            case "privatenet":
                mongodbConnStr = mh.mongodbConnStr_privatenet;
                mongodbDatabase = mh.mongodbDatabase_privatenet;
                neoCliJsonRPCUrl = mh.neoCliJsonRPCUrl_privatenet;
                break;
        }
    }
    

    打完收工我们来试一下,CTRL+F5,我们可以看到内置的 IIS Express 被启动了,可以访问这个地址:
    http://localhost:59908/api/privatenet
    显示的结果是:
    {"jsonrpc":"2.0","id":0,"error":{"code":-100,"message":"Parameter Error","data":"Value cannot be null. Parameter name: s"}}
    那说明 webapi 本身已经没问题了,我们看到用的是 localhost ,如果想局域网都能用的话我们可以改一下 .vsconfigapplicationhost.config 里面的配置再运行,或者我们用典型的 .net core 方法(我采用这种),就是在 Program.cs 里面加一点料:.UseUrls("http://*:59908"),详细如下,这是允许局域网 ip 访问的第一步:

    public static IWebHost BuildWebHost(string[] args) =>
                WebHost.CreateDefaultBuilder(args)
                    .UseStartup<Startup>()
                    .UseUrls("http://*:59908")
                    .Build();
    

    第二步我们改一下启动方式,先重新生成解决方案,然后把 appsettings.jsonmongodbsettings.json 两个文件复制到 binDebug etcoreapp2.0 里面去,关掉刚才启动的 IIS Express,然后我们以管理员身份打开 cmd ,cd 到你的 binDebug etcoreapp2.0 文件夹里面,然后 dotnet NEO_Block_API.dll 就好了,结果如下:
    dotnet-neo-block-api
    你可以去局域网的其他机器上用 postman 访问一下,没啥问题,用 getblockcount 接口吧,路由是这个: http://192.168.1.151:59908/api/privatenet ,文档在这:单击此处
    我在这台 windows 10 上操作的,IP 是 192.168.1.151 ,所以我们得到了第二个想要的接口地址,就是 API for UTXO 的 api 地址:http://192.168.1.151:59908/api/privatenet

    thinWallet 接入私链

    现在我们来观察一下 thinWallet 的代码结构:
    thinWallet-code-structure
    很显然,上面三个项目都是通用库,第四个是用户端,而且我们只需要改一个地方就可以了,双击打开 Window_thinwallet.xaml ,WPF 的界面文件,打开是这样的:
    wpf-full
    简直不能忍,不仅丑爆了,还影响阅读和写代码,我们做一下调整,下面图里面中间,左右两个红圈圈出来的按钮,先按左边那个,上下窗口对调,然后按右边那个,把下窗口缩进去,这样就是纯代码界面了,又好看又能高效阅读。
    wpf-adjust
    接下来我们要把它接入私链,其实就改两个接口地址,就在这个界面代码里面,thinWallet 对这两个地址目前是写死在界面上的,我们在界面代码里面查找一下 CTRL+F ,输入 47.96 就能找到了,一共两个地方,分别对应的填进去:
    private-api
    打完收工我们来访问一下,CTRL+F5:
    thinWallet-error-height
    我们发现一个很诡异的数字,下面那个直接访问 neo rpc 的已经完全正常了,但是上面那个访问 webapi 的居然是这么个诡异的数字,那么碰到问题,我们首先得想到的事情只有一件,就是 F5,首先我们先打开 Window_thinwallet.xaml 这个文件的后台文件,就是 Window_thinwallet.xaml.cs ,我们观察一下里面的代码,发现有一个异步更新界面的地方:
    update-ui-height
    再来看一下这个 api_getHeight 是怎么实现的,F12 过去:
    update-ui-request
    真相在这里,请求了 webapi 的一个 getblockcount 的接口,那么现在来看一下 NEO_Block_API 项目对 getblockcount 是怎么实现的:
    impl-getblockcount
    这时候就发现了,这里居然在调用 mongodb 的数据,我们回想一下,似乎没有任何操作 mongodb 的地方,也就是说没有任何数据,那么我们可以猜到,getblockcount 接口返回了一个 0 ,而且在我们的 Window_thinwallet.xaml.cs 文件异步更新界面的地方,最后 var height = ulong.Parse(json[0].AsDict()["blockcount"].ToString()) - 1; 来了这么一句,很明确了,无符号长整型,减了个 1 ,导致变成了那个数字,现在来 F5 证明一下,断点打在 api_getHeight 这个方法的最后一句 return 的代码上:
    debug
    其中 result 是请求结果,就是 0 ,已经说明了一切。那还差什么呢,想想我们好像漏了一个项目,没错还有最后一个调度任务项目没配置好。

    NeoBlock-Mongo-Storage 开启对私链的任务调度

    老样子观察代码:
    storage-structure
    很简单,一个入口,两个数据接口,一个工具,没了。
    我们可以发现在 Program.cs 里面有这么一段:

    var config = new ConfigurationBuilder()
        .AddInMemoryCollection()    //将配置文件的数据加载到内存中
        .SetBasePath(System.IO.Directory.GetCurrentDirectory())   //指定配置文件所在的目录
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)  //指定加载的配置文件
        .Build();    //编译成对象  
    mongodbConnStr = config["mongodbConnStr"];
    mongodbDatabase = config["mongodbDatabase"];
    NeoCliJsonRPCUrl = config["NeoCliJsonRPCUrl"];
    sleepTime = int.Parse(config["sleepTime"]);
    if (int.Parse(config["utxoIsSleep"]) == 1) {
        utxoIsSleep = true;
    }
    

    很简单了,加个文件,appsettings.json :

    {
      "mongodbConnStr": "mongodb://127.0.0.1:27017",
      "mongodbDatabase": "privatenet",
      "NeoCliJsonRPCUrl": "http://192.168.1.135:20332",
      "sleepTime": "0",
      "utxoIsSleep": "0"
    }
    

    同样道理,重新生成解决方案,把这个 json 文件复制到 binDebug etcoreapp2.0 里面去,然后管理员身份打开 cmd ,然后 cd 进去,然后 dotnet NeoBlockMongoStorage.dll ,看下结果:
    GIF
    这个狂暴的气息,然后我们开一下 thinWallet,看下结果:
    update-height
    这个高度正在快速地向最高高度同步过去,同步完成之后,我们就完成了 thinWallet 接入私链的所有步骤,你的 thinWallet 就可以在私链中随便玩了。
    同步完成之后,我们最好把 NeoBlock-Mongo-Storage 项目的 appsettings.json 改一下,把 sleepTime 从 0 改为 2000,utxoIsSleep 从 0 改为 1,因为调度是个无限循环,如果不让它睡一会,就会一直浪费你的 cpu ,同步完成了,没那么多数据要写了,让它休息休息,这样造成的效果是,上面那个高度永远比下面那个高度更新慢半拍。单位是毫秒。自己随便调节。

    {
      "mongodbConnStr": "mongodb://127.0.0.1:27017",
      "mongodbDatabase": "privatenet",
      "NeoCliJsonRPCUrl": "http://192.168.1.135:20332",
      "sleepTime": "2000",
      "utxoIsSleep": "1"
    }
    
  • 相关阅读:
    java不定参数列表---乔老师没讲,但是传智有讲
    java数据库连接模板代码通用收集
    java数据库连接模板代码通用收集
    BZOJ2060: [Usaco2010 Nov]Visiting Cows 拜访奶牛
    BZOJ1598: [Usaco2008 Mar]牛跑步
    BZOJ1710: [Usaco2007 Open]Cheappal 廉价回文
    manacher模板
    BZOJ1584: [Usaco2009 Mar]Cleaning Up 打扫卫生
    BZOJ1753: [Usaco2005 qua]Who's in the Middle
    BZOJ1828: [Usaco2010 Mar]balloc 农场分配
  • 原文地址:https://www.cnblogs.com/JoiT/p/thinwallet-to-private-neo-chain.html
Copyright © 2020-2023  润新知