最小可行性区块链设计系列:Day 5 数据序列化

前言

《最小可行性区块链设计系列》的第四讲(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 )。

下一讲我们将设计网络通讯功能。

    原文作者:大鱼Whale
    原文地址: https://www.jianshu.com/p/d39138a7a1ce
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞