如何创建NEO轻客户端

486 阅读10分钟

目前很多开发者身体力行的投入到NEO技术社区生态的建设当中,比如neo-swift,它是一个可实现与NEO区块链交互功能的轻客户端,主要用Swift-4语言编写。尽管它目前是一个轻客户端,但不代表以后就不会扩展为全节点客户端。

本文梳理了neo-swift的运作需要哪些支持,希望大家在读完这篇博客后能了解区块链的内里运作机制,也希望本文能激励开发者在NEO区块链上创建下一个项目。

为什么?

对于任何项目而言,第一个问题永远是项目的创建目的的是什么,neo-swift项目创建主要出于4个原因。

1.开发者想要开发分布式iOS应用,且应用广度越来越广。

2. 建立一个开发者友好社区,其中一个方法就是创建便于非区块链开发者使用的SDK。

3.有的开发者很喜欢Swift。

4.移动设备的开发与部署日益重要。



以下为它的实质内容

读取操作

首先需要实现区块链的读取操作。区块链是一个公开的分布式账本,用户无需通过任何形式的鉴定即可自由读取信息。实现此类读取操作的一个途径是在机器上运行全节点,全节点可以保存完全同步的区块链备份,但移动设备上显然还没有足够的空间存储区块链的全部信息备份。在这种情况下,读取数据的方式是通过RPC与运行全节点的人进行交流。

    public func getBlockBy(index: Int64, completion: @escaping (NeoClientResult<Block>) -> ()) {
        sendRequest(.getBlock, params: [index, 1]) { result in
            switch result {
                case .failure(let error):
                    completion(.failure(error))
                case .success(let response):
                    let decoder = JSONDecoder()
                    guard let data = try? JSONSerialization.data(withJSONObject: (response["result"] as! JSONDictionary), options: .prettyPrinted),
                        let block = try? decoder.decode(Block.self, from: data) else {
                            completion(.failure(.invalidData))
                            return
                    }
                
                let result = NeoClientResult.success(block)
                completion(result)
            }
        }
    }

这一组代码可以实现从链上的特定高度获取一个区块,写这篇文章时的区块高度为1,348,910,可以检索并查看任何低于该高度的区块。感兴趣的话可以在链上搜索交易、区块以及地址等信息,从而更深入了解数据结构的运作机制。https://neotracker.io/

创建钱包

尽管从区块链读取数据便于收集数据,但将数据写入区块链更有意思。本文将探讨NEO区块链目前正在使用的两个主要的写入操作,即发送资产与认领GAS。希望大家在通读本文后能对目前使用频率最高的几个区块链操作有清楚的了解。要想把信息写入NEO区块链,首先需要获得一对有效的公私钥授权你的写入操作。

“钱包实际上并不存储任何资产,它的功能只是使用私钥将写入需求发送给区块链。”

这是钱包软件用户最可能出现理解误区的一个地方。实际上,NEO资产是存储在区块链上的;钱包只是一个交互接口,用户可以通过这个交互接口输入私钥并移动资产,即:将信息写入分布式账本。

那么如何生成这对公私钥呢?首先得生成私钥,私钥是由64个字符组成的十六进制字符串,可以是0与2^256(1.15792089e77)之间的任一数字。“账户”的其余信息都是根据这串数字派生而来,账户信息包括私钥、WIF(钱包导入格式)、公钥与地址。

WIF

这是一个私钥

0C28FCA386C7A227600B2FE50B7CAEEC86D3BF1FBE471BE89827E19D72AA1D

由于过于复杂,把私钥转变为WIF(钱包导入格式)。

5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ

虽然仍未达到完全可读的程度,但肯定比原字符串更易读。WIF还有基本错误检查功能,所以如果把资产发送给WIF格式地址,就更有可能检查出错误。

公钥

成功获得私钥和WIF后,操作才开始复杂起来。将整个客户端搭建在Swift上很难,Swift没有加密方法库来实现椭圆曲线加密,而NEO需要使用secp256r1椭圆曲线来派生公钥。

可以通过下方链接深入了解有关信息:

ellipticcurves - Is secp256r1 more secure than secp256k1? - Cryptography Stack Exchange

https://github.com/neo-project/neo/blob/master/neo/Cryptography/Crypto.cs

使用btckeygenie,修改后就可以导入到golang钱包生成器中,并使用gomobile编译。未来可能能用Swift语言编写一个成熟的ECDSA(椭圆曲线数字签名算法)库,但现阶段还不能达到。

地址

获取公钥后,只要再获取地址就可以拥有一个“完整账户”了。在此阶段,将十六进制或WIF私钥放入go语言程序包中,生成一个完整的账户。

public init?(wif: String) {
    var error: NSError?
    guard let wallet = GoNeowalletGenerateFromWIF(wif, &error) else { return nil }
    self.wif = wif
    self.publicKey = wallet.publicKey()
    self.privateKey = wallet.privateKey()
    self.address = wallet.address()
    self.hashedSignature = wallet.hashedSignature() //We'll discuss this later
}

完成了以上步骤,实际上也就完成了SDK开发的第一步,也是重要的一步。

发送资产与认领GAS

生成钱包后,资产可以转入钱包地址,但同时还需要Neon钱包一类的应用来发送资产或认领GAS,当然我们还希望创建一个可以嵌入任何iOS移动应用的SDK。

这就需要实现sendrawtransaction RPC方法,参考文档。

RPC方法负载如下:

"params": [ "80000001195876cb34364dc38b730077156c6bc3a7fc570044a66fbfeeea56f71327e8ab0000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500c65eaf440000000f9a23e06f74cf86b8827a9108ec2e0f89ad956c9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50092e14b5e00000030aab52ad93f6ce17ca07fa88fc191828c58cb71014140915467ecd359684b2dc358024ca750609591aa731a0b309c7fb3cab5cd0836ad3992aa0a24da431f43b68883ea5651d548feb6bd3c8e16376e6e426f91f84c58232103322f35c7819267e721335948d385fae5be66e7ba8c748ac15467dcca0693692dac"]

出处:

官方文件->NetworkProtocol · neo-project/neo Wiki · GitHub

若结果为“
true
”,就意味着当前交易就已经成功向全网广播,大功告成!

若结果为“
false
”,就意味着当前交易未能成功广播,导致这种情况的原因有许多,如双重支付、签名不完整等。在此例中,交易经确认后已经成功广播,但是由于存在双重支付,因此未能进行第二次广播。

最终javascript sdk输出的十六进制字符串终于自己的字符串会一致。

跟着javascript操作一遍后,就能为发送资产交易的完整内存配置绘制一个一目了然的示意图,如下:

如果详细地浏览每一部分,就会发现交易从高度最低的内存地址(0x00)开始,一直到最高的内存地址(0xXX),在这里XX是以十六进制表示的交易长度。

  • 第一个元数据:包括交易的种类、版本与属性。交易种类是转账/80/,版本为/00/(可能需要升级?)这里所说的交易不包含特定的属性。

  • Input:Input才是真正有意思的部分。NEO或任一区块链系统的余额不同于传统服务器客户端模型中数据库的任何数字,在实际操作中,余额是以以下形式呈现的。

"GAS": {
        "balance": 29.02751232,
        "unspent": [
            {
                "index": 1,
                "txid": "74cc80ffb1588a964fc6a656302bfe5c3465d2214c64211d8fe2f322cb342a29",
                "value": 28.0
            },
            {
                "index": 0,
                "txid": "819e00aeca6be42a436b9535b2c165670be9011bbd41afe5d475b0c858a7f6c5",
                "value": 1.0
            },
            {
                "index": 1,
                "txid": "3d96fb31185394147b237a987730fa2e4c1848744530842e468a6d9bdeec4069",
                "value": 0.02751232
            }
        ]
    }

余额总数是某交易ID下“未消耗”数列中所有对象的总合。当我们发送GAS等资产时,就需要使用这些未消耗的Input来生成一个结果。例如,如果需要发送28个GAS,就需要一个值为28的Input,但如果需要发送29个GAS,就需要两个Input,一个值为28,另一个值为1。你看,所有的交易都会归纳为一个任意数值。目前能进行的最大的交易就是发送30个GAS,这需要3个Input。

  • 第二个元数据:表示Output的次数。例如,如果我们想要发送28个GAS,仅会产生一个Output,因为我们会消耗所有Output,仅留下一个Output,这个Output即为发送给接收人的新交易信息。但如果想要发送27个GAS呢,情况就会更加复杂,因为这时会产生两个Output,一个Output是发送至接收人的27个GAS,另一个是发回给发送人的找零。所以每个交易都会产生1个或2个Output。

  • 资产ID:NEO网络里的每个资产都有其独一无二的标识符,目前有两个标识符,但未来可能会有无数个标识符。

  • 发送资产数额:这是不言自喻的,需要乘以100000000验证浮点数精度

  • 接收人地址哈希值:这也是不言自喻的,代表接收人地址的哈希值

  • Total-Send总计:这是发回给钱包地址的找零,如果找零为0,你就不需要填写这个字段或后面的发送人地址哈希值

  • 发送人地址哈希值:接收找零的地址哈希值

签名:继续使用同一个p256椭圆曲线来为负载签名,这就是第三个元数据块前的所有内容。

  • NEO公钥:即为普通的公钥,但包括前缀与后缀字节,我也不清楚前缀和后缀的用处,可能有检查错误的功能。

结论

区块链是让人兴奋的,它并不只是一门简单的技术,想要打通与未来世界的连接的桥梁,NEO是一个绝佳的切入点。NEO作为一个开源项目,开发者越来越多,生态越来越健康,希望能够加入到NEO社区建设中来,NEO有机会成为dBFT共识机制与区块链技术的黄金标准。


感谢本文原作者Salty Skip

原文