使用OmniCore在Unix系统上构建USDT钱包(二)

作者: MatrixYe | 来源:发表于2018-12-11 18:46 被阅读816次
    image

    本篇是关于“如何使用OmniCore在服务端构建USDT钱包”系列的下篇,主要是对上一篇《使用OmniCore构建USDT钱包(一)》的补充和总结。上一篇主要对安装、配置和基本json-api使用,以及交易所钱包模型做了简略的说明,这一篇将对常见错误、安全机制以及其他扩展做一些技术分享。

    1.omni_funded_send -212错误

    从v0.3.1开始omnicore提供了新的转账api omni_funded_send 来支持USDT的转账操作。与之前的方式不同在于,omni_funded_send支持从第三方支付手续费。但在实际操作,明明拥有充足的btc用于支付手续费,也有充分的usdt用于转账,还是会频繁出现编号-212错误:

    $ ./omnicore-cli omni_funded_send "mrAVAPxdQEZxFkunh56skB6sgJa6vrfrpo" "msJ2h47ZrxFJjksVvPy8ik4h2HFfa9W1zV" 31 "100.01" "mpaumxor659PhoJhXp1VCVHVwbFCZSRmuf"
    error code: -212
    error message:
    Error choosing inputs for the send transaction
    

    issues/760#详细讨论了这个问题。

    USDT的转账本质上属于比特币的一种特殊交易(OP_RETUTRN),需要一部分BTC来支付矿工费用。同时,根据omnilayer协议,还需要微量的比特币用于标记omni事务的接受者,这部分btc无法从手续费地址扣除,只能从发送地址扣除。因此,当发送地址缺乏btc(也就是UTXO)时,会出现 “Error choosing inputs for the send transaction”的错误。经过测试,默认情况下用于标记omni事务的BTC为0.0000546。因此解决这个问题的唯一方法是向发送地址中转入>0.0000546的BTC。

    但需要注意的是,发送地址上的某个UTXO将全部消耗掉,不会有找零。例如A地址向B地址转移USDT,A地址中拥有两个UTXO,分别为0.0001和0.0002,那么转账成功后,0.0001的UTXO将会被消耗掉,只剩下额度为0.0002的UTXO。

    但在交易所模式下,这种错误几乎不会发生。因为在中心化钱包的模式下,充值的过程会自动携带了微量的btc,这一部分比特币足够用于标记omni事务,用于下一次转账,但也仅限一次。因此,只要不使用同一个地址发送两次USDT,这种错误几乎不会出现。

    2. 钱包安全

    数字资产不同于一般性的资产,一旦被盗或丢失,几乎无法找回,也无法进行数据回滚。对于中心化的钱包而言,用户本身对资产没有直接控制权,所有的资产安全性全部取决于中心化钱包的安全策略,因此非常有必要对安全性进行设计。以下方式仅供参考。

    2.1 限制访问权限

    omnicore基于bitcore,自带有加密访问权限设置,可以通过bitcoin.conf文件可以进行配置。

    rpcuser=你的rpc用户名
    rpcpassword=你的rpc密码
    rpcallowip=127.0.0.1 
    rpcport=8332
    

    其中rpcuserrpcpassword分别配置访问用户和访问密码,rpcallowiprpcport分别配置可访问的网络地址。测试环境下,该地址可以为0.0.0.0/0 为全地址访问。正式环境下,需要配置为制定的访问服务器地址或者本地地址。值得注意的是,这里的安全原理是限定钱包访问对象,一旦密码泄漏或服务器被黑,那么整个钱包相当于裸奔。

    2.2 离线生成地址

    资产的唯一凭证是私钥,因此私钥的保存非常重要。一般情况下,根据业务需求,服务端需要给每个用户分配一个地址。这里的建议是不应该分配地址的时候“即使”的去调用getnewaddresss,而是通过离线的方式批量生产大量地址,然后连同地址、私钥保存到数据库,同时导出地址列表给服务端进行使用。这样服务端需要分配地址时,只需要从数据库读取即可,而不需要与钱包进行交互。

    为了私钥的安全性,保存到数据库时可以进行二次加密。

    2.3 钱包文件备份

    钱包文件的备份可以直接使用bitcore的导出数据方式dumpwallet,每天定期保存到文件,例如:

      /***
         * 导出钱包数据以人类可读的方式
         * @param: []
         * @return: java.lang.String
         **/
        public String dumpWallet() {
            System.currentTimeMillis();
            String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            String file = String.format("wallet-%s.txt", time);
            http.engine("dumpwallet", file);
            return file;
        }
    

    一旦需要恢复,使用importwallet即可,例如:

    /***
         * 钱包数据导入
         * @param: []
         * @return: void
         * https://bitcoin.org/en/developer-reference#importwallet
         *对于影响新添加的密钥的事务,调用可能需要重新扫描整个链,可能需要几分钟。
         **/
        public void importWallet(String fielName) {
            http.engine("importwallet", fielName);
        }
    
    

    2.4 冷钱包

    冷钱包即不联网的钱包。对于交易所模式下而言,钱包的作业其实就相当于银行的存款系统,充值的过程相当于把金额全部汇总到中央地址。因此可以使用一台不联网的钱包生成中央地址若干,将私钥保存到U盘或其他加密硬件中,充值金额将汇集到冷钱包中保存。一旦需要提现,可以根据金额大小。小额提现可以通过热钱包直接发消息转账;大额可以使用需要使用指定U盘将私钥或钱包文件导入到钱包中,然后联网转账。操作完成后断网,重新保存硬件即可。

    这一过程需要注意,冷钱包因为无法联网,自然也无法知道余额。那么可以设置观察钱包对中央钱包地址进行余额观察,以确保充值到账。在实际业务中,提现过程可能还需要人工进行审核。

    2.5 钱包与服务端隔离

    一般情况下,钱包与服务端会部署在同一台服务器上。这种方式有两个缺陷,第一是钱包需要的存储空间庞大,但需要的网络带宽很低,与业务相关的服务器部署子啊同一服务器上将造成资源的浪费。第二是这种方式有一些安全隐患。所以建议钱包单独部署到一台服务器上,如果业务允许,还可以同时备份到多台冗余服务器上。钱包与服务端的通讯可以使用 “消息队列”。

    注意使用Queue,而不是Topics,同一时间内,只有一个消费者和一个生产者。

    image

    如果文章的内容对你有所帮助,希望你能点赞、投币、收藏三连。
    如果你对区块链技术有兴趣,可以加入我们在杭州的交流群。

    相关文章

      网友评论

        本文标题:使用OmniCore在Unix系统上构建USDT钱包(二)

        本文链接:https://www.haomeiwen.com/subject/vviehqtx.html