前言
《最小可行性区块链设计系列》的第四讲(http://www.jianshu.com/p/608cfb46ff49) 实现了挖矿机制。
本文的代码地址:https://github.com/qikh/mini-block-chain/commit/00e44cf18bf497535441c2b42e6ce19c9ae552e9 (开发语言为Kotlin,更简洁的Java)
正文
POW共识算法(Consensus)的算力计算部分已经完成,我们需要继续实现多个区块链节点之间通过网络通讯达成数据共识的功能。
在区块(Block)和交易(Transaction)数据的通讯需要定义标准化的数据序列化规范,帮助不同语言编写的区块链客户端之间交互数据。比特币定义了简单的二进制序列化标准(https://en.bitcoin.it/wiki/Protocol_documentation#Common_structures ), 以太坊则设计了一套RLP编码规范(https://github.com/ethereum/wiki/wiki/RLP ),MiniBlockChain最后选择了ASN.1编码规范作为数据序列化规范(ASN.1是非常完善的一套数据序列化规范,是密码学领域的编码标准,支持各类数据结构,类库的支持也非常成熟,http://luca.ntop.org/Teaching/Appunti/asn1.html )。
有三种数据类型需要在客户端之间通讯并序列化,分别为账户状态(Account State)、交易(Transaction)数据和区块(Block)数据。
账户状态(Account State)的序列化:
/**
* 序列化账户状态(Account State)。(使用ASN.1规范)
*/
fun encodeAccountState(accountState: AccountState): ByteArray {
val v = ASN1EncodableVector()
v.add(ASN1Integer(accountState.nonce))
v.add(ASN1Integer(accountState.balance))
return DERSequence(v).encoded
}
/**
* 反序列化账户状态(Account State)。(使用ASN.1规范)
*/
fun decodeAccountState(bytes: ByteArray): AccountState? {
val v = ASN1InputStream(bytes)?.readObject()
if (v != null) {
val seq = ASN1Sequence.getInstance(v)
val nonce = ASN1Integer.getInstance(seq.getObjectAt(0))?.value
val balance = ASN1Integer.getInstance(seq.getObjectAt(1))?.value
if (nonce != null && balance != null) {
return AccountState(nonce, balance)
}
}
return null
}
交易(Transaction)数据的序列化
/**
* 序列化交易(Transaction)。(使用ASN.1规范)
*/
fun encodeTransaction(trx: Transaction): ByteArray {
val v = ASN1EncodableVector()
v.add(DERBitString(trx.senderAddress))
v.add(DERBitString(trx.receiverAddress))
v.add(ASN1Integer(trx.amount))
v.add(ASN1Integer(trx.time.millis))
v.add(DERBitString(trx.publicKey.encoded))
return DERSequence(v).encoded
}
/**
* 反序列化交易(Transaction)。(使用ASN.1规范)
*/
fun decodeTransaction(bytes: ByteArray): Transaction? {
val v = ASN1InputStream(bytes)?.readObject()
if (v != null) {
val seq = ASN1Sequence.getInstance(v)
val senderAddress = DERBitString.getInstance(seq.getObjectAt(0))?.bytes
val receiverAddress = DERBitString.getInstance(seq.getObjectAt(1))?.bytes
val amount = ASN1Integer.getInstance(seq.getObjectAt(2))?.value
val millis = ASN1Integer.getInstance(seq.getObjectAt(3))?.value
val publicKeyBytes = DERBitString.getInstance(seq.getObjectAt(4))?.bytes
val kf = KeyFactory.getInstance("EC", "SC")
val publicKey = kf.generatePublic(X509EncodedKeySpec(publicKeyBytes))
if (senderAddress != null && receiverAddress != null && amount != null && millis != null) {
return Transaction(senderAddress, receiverAddress, amount, DateTime(millis.toLong()), publicKey)
}
}
return null
}
区块(Block)数据的序列化:
/**
* 序列化区块(Block)。(使用ASN.1规范)
*/
fun encodeBlock(block: Block): ByteArray {
val v = ASN1EncodableVector()
v.add(ASN1Integer(block.version.toLong()))
v.add(ASN1Integer(block.height))
v.add(DERBitString(block.parentHash))
v.add(DERBitString(block.coinBase))
v.add(DERBitString(block.merkleRoot))
v.add(ASN1Integer(block.difficulty.toLong()))
v.add(ASN1Integer(block.nonce.toLong()))
v.add(ASN1Integer(block.time.millis))
v.add(ASN1Integer(block.transactions.size.toLong()))
val t = ASN1EncodableVector()
block.transactions.map { t.add(ASN1InputStream(encodeTransaction(it)).readObject()) } // transactions
v.add(DERSequence(t))
return DERSequence(v).encoded
}
/**
* 反序列化区块(Block)。(使用ASN.1规范)
*/
fun decodeBlock(bytes: ByteArray): Block? {
val v = ASN1InputStream(bytes)?.readObject()
if (v != null) {
val seq = ASN1Sequence.getInstance(v)
val version = ASN1Integer.getInstance(seq.getObjectAt(0)).value
val height = ASN1Integer.getInstance(seq.getObjectAt(1)).value
val parentHash = DERBitString.getInstance(seq.getObjectAt(2))?.bytes
val minerAddress = DERBitString.getInstance(seq.getObjectAt(3))?.bytes
val merkleRoot = DERBitString.getInstance(seq.getObjectAt(4))?.bytes
val difficulty = ASN1Integer.getInstance(seq.getObjectAt(5))?.value
val nonce = ASN1Integer.getInstance(seq.getObjectAt(6))?.value
val millis = ASN1Integer.getInstance(seq.getObjectAt(7))?.value
val trxSize = ASN1Integer.getInstance(seq.getObjectAt(8))?.value
val trxValues = ASN1Sequence.getInstance(seq.getObjectAt(9))
val trxList = mutableListOf<Transaction>()
for (trxValue in trxValues.objects) {
val trxObj = DERSequence.getInstance(trxValue) ?: return null
val trx = decodeTransaction(trxObj.encoded) ?: return null
trxList.add(trx)
}
if (version == null || height == null || parentHash == null || minerAddress == null ||
merkleRoot == null || difficulty == null || nonce == null || millis == null) {
return null
}
return Block(version.toInt(), height.toLong(), parentHash, CryptoUtil.merkleRoot(trxList), minerAddress, trxList,
DateTime(millis.toLong()), difficulty.toInt(), nonce.toInt())
}
return null
}
序列化数据的存储方式通常是Key-Value格式,MiniBlockChain的数据存储模块设计为可配置,除了测试和调试用的内存存储(MemoryDataSource)外,还加入了LevelDB的支持,并且可以通过接口扩展来增加对其他数据库的支持。
MiniBlockChain增加了可配置化支持,和ethereumj一样采用了typesafe config的格式(https://github.com/typesafehub/config )。
下一讲我们将设计网络通讯功能。