还要集中于"数据存款和储蓄"这一环节

时间:2019-09-24 15:14来源:118最快开奖现场直播
据北京股票(stock)报新闻,华泰股票财力管理消息本事部管事人张铭锋新近表示,区块链的共同的认知机制能保险音信在传输进程中的透明性、完整性和及时性,幸免守旧数码分享形

据北京股票(stock)报新闻,华泰股票财力管理消息本事部管事人张铭锋新近表示,区块链的共同的认知机制能保险音信在传输进程中的透明性、完整性和及时性,幸免守旧数码分享形式下的多寡篡改。

图片 1

那多少个加入方众多、贫乏聚集交易地方及信息分享机制的交易品种,如单据业务、衍生品交易等,都适用区块链。

最后内容请以原来的小说为准:

在这一名目好些个小说的最发轫有个别,大家关系过区块链是一个布满式的数据库。那时候,大家决定跳过"遍及式"这一环节,并且集中于"数据存款和储蓄"这一环节。到近来截至,大家大致达成了区块链的所有组成都部队分。在本篇文章中,我们将会提到一些在最近的篇章中所忽略的片段体制,并且在下一篇文章中大家将启幕研商区块链的分布式个性。

前方种种部分剧情:

  1. 中央原型
  2. 专业量注脚
  3. 持久化 & 命令行
  4. 交易
  5. 地址

在 长久化 & 命令行 那篇小说中,我们研讨了比特币宗旨存款和储蓄区块的格局。个中大家提到过与区块相关的数量存款和储蓄在 blocks 这么些数目桶中,而交易数据则存款和储蓄在 chainstate 这些数量桶中,让大家来回看一下,chainstate 数据桶的数据结构:

  • 'c' + 32-byte transaction hash -> unspent transaction output record for that transaction

    某笔交易的UTXO记录

  • 'B' -> 32-byte block hash: the block hash up to which the database represents the unspent transaction outputs

    数据库所表示的UTXO的区块Hash

从那篇小说初始,大家早就完毕了比特币的贸易机制,可是大家还从未用到 chainstate 数据桶去存款和储蓄大家的贸易输出。所以,那将是我们后天要去做的工作。

chainstate 不会去存储交易数据。相反,它存款和储蓄的是 UTXO 集,约等于未被成本的贸易输出集结。除了这些之外,它还蕴藏了"数据库所代表的UTXO的区块Hash",大家这里先权且忽略那一点,因为我们还未曾用到区块高度(这点大家会在末端的稿子进行落实)。

那就是说,大家为啥须求 UTXO 池呢?

一路来看一下大家前面完结的 findUnspentTransactions 方法:

 /** * 查找钱包地址对应的所有未花费的交易 * * @param pubKeyHash 钱包公钥Hash * @return */ private Transaction[] findUnspentTransactions(byte[] pubKeyHash) throws Exception { Map<String, int[]> allSpentTXOs = this.getAllSpentTXOs(pubKeyHash); Transaction[] unspentTxs = {}; // 再次遍历所有区块中的交易输出 for (BlockchainIterator blockchainIterator = this.getBlockchainIterator(); blockchainIterator.hashNext { Block block = blockchainIterator.next(); for (Transaction transaction : block.getTransactions { String txId = Hex.encodeHexString(transaction.getTxId; int[] spentOutIndexArray = allSpentTXOs.get; for (int outIndex = 0; outIndex < transaction.getOutputs().length; outIndex++) { if (spentOutIndexArray != null && ArrayUtils.contains(spentOutIndexArray, outIndex)) { continue; } // 保存不存在 allSpentTXOs 中的交易 if (transaction.getOutputs()[outIndex].isLockedWithKey(pubKeyHash)) { unspentTxs = ArrayUtils.add(unspentTxs, transaction); } } } } return unspentTxs; }

该方法是用来探索卡包地址对应的盈盈未开销交易输出的贸易音讯。由于贸易消息是积累在区块其中,所以大家现存的做法是遍历区块链中的各类区块,然后遍历种种区块中的交易音信,再然后遍历每一种交易中的交易输出,并检讨交易输出是不是被相应的卡包地址所锁定,功效相当的低下。结束二零一八年11月29号,比特币中有 515698 个区块,而且那一个多少攻陷了140+Gb 的磁盘空间。那也就象征一人总得运营全节点(下载全体的区块数据)技能表明交易新闻。另外,验证交易消息须要遍历全体的区块。

本着这么些题指标解决办法是内需有三个仓储了颇具UTXOs的目录,那正是 UTXOs 池所要做的政工:UTXOs池其实是叁个缓存空间,它所缓存的数量须求从创设区块链中全体的贸易数据中得到(通过遍历全体的区块链,然则那一个营造操作只必要执行一遍就能够),并且它一而再还恐怕会用来钱袋余额的计量以及新的贸易数额的表明。甘休到前年四月,UTXOs池大概为 2.7Gb。

好了,让大家来想转手,为了落实 UTXOs 池大家要求做什么样事情。当前,有下列方法被用于查找交易音讯:

  1. Blockchain.getAllSpentTXOs —— 查询全体已被花费的贸易输出。它要求遍历区块链中全部区块中交易音信。

  2. Blockchain.findUnspentTransactions —— 查询富含未被花费的交易输出的贸易消息。它也急需遍历区块链中全体区块中交易音讯。

  3. Blockchain.findSpendableOutputs —— 该措施用于新的交易创造之时。它供给找到丰富多的贸易输出,以知足所需付出的金额。供给调用 Blockchain.findUnspentTransactions 方法。

  4. Blockchain.findUTXO —— 查询钱袋地址所对应的保有未费用交易输出,然后用于总括钱袋余额。需求调用

    Blockchain.findUnspentTransactions 方法。

  5. Blockchain.findTransaction —— 通过交易ID查询交易音信。它必要遍历全数的区块直到找到交易消息甘休。

如您所见,上边那些办法都亟待去遍历数据库中的全体区块。由于UTXOs池只存款和储蓄未被花费的交易输出,而不会蕴藏全部的交易音信,由此大家不会对有 Blockchain.findTransaction 实行优化。

那正是说,我们必要下列那个点子:

  1. Blockchain.findUTXO —— 通过遍历全数的区块来找到全数未被花费的交易输出.
  2. UTXOSet.reindex —— 调用上边 findUTXO 方法,然后将查询结果存款和储蓄在数据库中。也即必要张开缓存的地点。
  3. UTXOSet.findSpendableOutputs —— 与 Blockchain.findSpendableOutputs 类似,差距在于会利用 UTXO 池。
  4. UTXOSet.findUTXO —— 与Blockchain.findUTXO 类似,差别在于会动用 UTXO 池。
  5. Blockchain.findTransaction —— 逻辑保持不变。

如此那般,四个使用最频仍的不二诀要将从未来上马运用缓存!让大家初叶编码吧!

定义 UTXOSet

@NoArgsConstructor@AllArgsConstructor@Slf4jpublic class UTXOSet { private Blockchain blockchain;}

重建 UTXO 池索引:

public class UTXOSet { ... /** * 重建 UTXO 池索引 */ @Synchronized public void reIndex() { log.info("Start to reIndex UTXO set !"); RocksDBUtils.getInstance().cleanChainStateBucket(); Map<String, TXOutput[]> allUTXOs = blockchain.findAllUTXOs(); for (Map.Entry<String, TXOutput[]> entry : allUTXOs.entrySet { RocksDBUtils.getInstance().putUTXOs(entry.getKey(), entry.getValue; } log.info("ReIndex UTXO set finished ! "); } ...} 

此格局用于初阶化 UTXOSet。首先,供给清空 chainstate 数据桶,然后查询全体未被花费的交易输出,并将它们保存到 chainstate 数据桶中。

实现 findSpendableOutputs 方法,供 Transation.newUTXOTransaction 调用

public class UTXOSet { ... /** * 寻找能够花费的交易 * * @param pubKeyHash 钱包公钥Hash * @param amount 花费金额 */ public SpendableOutputResult findSpendableOutputs(byte[] pubKeyHash, int amount) { Map<String, int[]> unspentOuts = Maps.newHashMap(); int accumulated = 0; Map<String, byte[]> chainstateBucket = RocksDBUtils.getInstance().getChainstateBucket(); for (Map.Entry<String, byte[]> entry : chainstateBucket.entrySet { String txId = entry.getKey(); TXOutput[] txOutputs = (TXOutput[]) SerializeUtils.deserialize(entry.getValue; for (int outId = 0; outId < txOutputs.length; outId++) { TXOutput txOutput = txOutputs[outId]; if (txOutput.isLockedWithKey(pubKeyHash) && accumulated < amount) { accumulated += txOutput.getValue(); int[] outIds = unspentOuts.get; if (outIds == null) { outIds = new int[]{outId}; } else { outIds = ArrayUtils.add(outIds, outId); } unspentOuts.put(txId, outIds); if (accumulated >= amount) { break; } } } } return new SpendableOutputResult(accumulated, unspentOuts); } ... } 

实现 findUTXOs 接口,供 CLI.getBalance 调用:

public class UTXOSet { ... /** * 查找钱包地址对应的所有UTXO * * @param pubKeyHash 钱包公钥Hash * @return */ public TXOutput[] findUTXOs(byte[] pubKeyHash) { TXOutput[] utxos = {}; Map<String, byte[]> chainstateBucket = RocksDBUtils.getInstance().getChainstateBucket(); if (chainstateBucket.isEmpty { return utxos; } for (byte[] value : chainstateBucket.values { TXOutput[] txOutputs = (TXOutput[]) SerializeUtils.deserialize; for (TXOutput txOutput : txOutputs) { if (txOutput.isLockedWithKey(pubKeyHash)) { utxos = ArrayUtils.add(utxos, txOutput); } } } return utxos; } ... } 

以上那几个方法都以先前 Blockchain 中相应措施的微调版,先前的点子将不再利用。

有了UTXO池之后,意味着大家的交易数据分开累积到了八个差别的数目桶中:交易数据存款和储蓄到了 block 数据桶中,而UTXO存款和储蓄到了 chainstate 数据桶中。那就需求一种共同机制来保管每当三个新的区块发生时,UTXO池能够立时联合最新区块中的交易数据,终究大家不想频地进行 reIndex 。因而,大家必要如下方法:

更新UTXO池:

public class UTXOSet { ... /** * 更新UTXO池 * <p> * 当一个新的区块产生时,需要去做两件事情: * 1)从UTXO池中移除花费掉了的交易输出; * 2)保存新的未花费交易输出; * * @param tipBlock 最新的区块 */ @Synchronized public void update(Block tipBlock) { if (tipBlock == null) { log.error("Fail to update UTXO set ! tipBlock is null !"); throw new RuntimeException("Fail to update UTXO set ! "); } for (Transaction transaction : tipBlock.getTransactions { // 根据交易输入排查出剩余未被使用的交易输出 if (!transaction.isCoinbase { for (TXInput txInput : transaction.getInputs { // 余下未被使用的交易输出 TXOutput[] remainderUTXOs = {}; String txId = Hex.encodeHexString(txInput.getTxId; TXOutput[] txOutputs = RocksDBUtils.getInstance().getUTXOs; if (txOutputs == null) { continue; } for (int outIndex = 0; outIndex < txOutputs.length; outIndex++) { if (outIndex != txInput.getTxOutputIndex { remainderUTXOs = ArrayUtils.add(remainderUTXOs, txOutputs[outIndex]); } } // 没有剩余则删除,否则更新 if (remainderUTXOs.length == 0) { RocksDBUtils.getInstance().deleteUTXOs; } else { RocksDBUtils.getInstance().putUTXOs(txId, remainderUTXOs); } } } // 新的交易输出保存到DB中 TXOutput[] txOutputs = transaction.getOutputs(); String txId = Hex.encodeHexString(transaction.getTxId; RocksDBUtils.getInstance().putUTXOs(txId, txOutputs); } } ... } 

让大家将 UTXOSet 用到它们所需之处去:

public class CLI { ... /** * 创建区块链 * * @param address */ private void createBlockchain(String address) { Blockchain blockchain = Blockchain.createBlockchain; UTXOSet utxoSet = new UTXOSet(blockchain); utxoSet.reIndex(); log.info("Done ! "); } ... } 

当创立两个新的区块链是,我们供给重新建立 UTXO 池索引。停止方今,那是独一一处用到 reIndex 的地点,就算看起某个多余,因为在区块链创造之初仅仅独有三个区块和一笔交易。

修改 CLI.send 接口:

public class CLI { ... /** * 转账 * * @param from * @param to * @param amount */ private void send(String from, String to, int amount) throws Exception { ... Blockchain blockchain = Blockchain.createBlockchain; Transaction transaction = Transaction.newUTXOTransaction(from, to, amount, blockchain); Block newBlock = blockchain.mineBlock(new Transaction[]{transaction}); new UTXOSet(blockchain).update; ... } ... } 

当四个新的区块发生后,须要去创新 UTXO 池数据。

让我们来检查一下它们的周转景况:

$ java -jar blockchain-java-jar-with-dependencies.jar createwalletwallet address : 1JgppX2xMshr35wHzvNWQBejUAZ3Te5Mdf$ java -jar blockchain-java-jar-with-dependencies.jar createwalletwallet address : 1HX7bWwCjvxkjq65GUgAVRFfTZy6yKWkoG$ java -jar blockchain-java-jar-with-dependencies.jar createwalletwallet address : 1L1RoFgyjCrNPCPHmSEBtNiV3h2wiF9mZV$ java -jar blockchain-java-jar-with-dependencies.jar createblockchain -address 1JgppX2xMshr35wHzvNWQBejUAZ3Te5MdfElapsed Time: 164.961 seconds correct hash Hex: 00225493862611bc517cb6b3610e99d26d98a6b52484c9fa745df6ceff93f445 Done ! $ java -jar blockchain-java-jar-with-dependencies.jar getbalance -address 1JgppX2xMshr35wHzvNWQBejUAZ3Te5MdfBalance of '1JgppX2xMshr35wHzvNWQBejUAZ3Te5Mdf': 10$ java -jar blockchain-java-jar-with-dependencies.jar send -from 1HX7bWwCjvxkjq65GUgAVRFfTZy6yKWkoG -to 1JgppX2xMshr35wHzvNWQBejUAZ3Te5Mdf -amount 5java.lang.Exception: ERROR: Not enough funds$ java -jar blockchain-java-jar-with-dependencies.jar send -from 1JgppX2xMshr35wHzvNWQBejUAZ3Te5Mdf -to 1HX7bWwCjvxkjq65GUgAVRFfTZy6yKWkoG -amount 2Elapsed Time: 54.92 seconds correct hash Hex: 0001ab21f71ff2d6d532bf3b3388db790c2b03e28d7bd27bd669c5f6380a4e5b Success!$ java -jar blockchain-java-jar-with-dependencies.jar send -from 1JgppX2xMshr35wHzvNWQBejUAZ3Te5Mdf -to 1L1RoFgyjCrNPCPHmSEBtNiV3h2wiF9mZV -amount 2Elapsed Time: 54.92 seconds correct hash Hex: 0009b925cc94e3db8bab2958b1fc2d1764aa15531e20756d92c3a93065c920f0 Success!$ java -jar blockchain-java-jar-with-dependencies.jar getbalance -address 1JgppX2xMshr35wHzvNWQBejUAZ3Te5MdfBalance of '1JgppX2xMshr35wHzvNWQBejUAZ3Te5Mdf': 6$ java -jar blockchain-java-jar-with-dependencies.jar getbalance -address 1HX7bWwCjvxkjq65GUgAVRFfTZy6yKWkoGBalance of '1HX7bWwCjvxkjq65GUgAVRFfTZy6yKWkoG': 2$ java -jar blockchain-java-jar-with-dependencies.jar getbalance -address 1L1RoFgyjCrNPCPHmSEBtNiV3h2wiF9mZVBalance of '1L1RoFgyjCrNPCPHmSEBtNiV3h2wiF9mZV': 2

眼下的章节中我们简要了矿工挖矿的表彰机制。机遇已经成熟,该兑现它了。

矿工表彰其实是贰个 coinbase 交易。当一个矿工节点初叶去生产二个新的区块时,他会从队列中取出一些贸易数据,并且为它们预制一个coinbase 交易。那笔 coinbase 交易中仅局地交易输出包蕴了矿工的公钥hash。

只须求更新 send 命令接口,大家就能够轻巧落成矿工的奖励机制:

public class CLI { ... /** * 转账 * * @param from * @param to * @param amount */ private void send(String from, String to, int amount) throws Exception { ... Blockchain blockchain = Blockchain.createBlockchain; // 新交易 Transaction transaction = Transaction.newUTXOTransaction(from, to, amount, blockchain); // 奖励 Transaction rewardTx = Transaction.newCoinbaseTX; Block newBlock = blockchain.mineBlock(new Transaction[]{transaction, rewardTx}); new UTXOSet(blockchain).update; ... } ... } 

还亟需修改交易认证办法,coinbase 交易向来评释通过:

public class Blockchain { /** * 交易签名验证 * * @param tx */ private boolean verifyTransactions(Transaction tx) { if (tx.isCoinbase { return true; } ... } ... } 

在我们的兑现逻辑中,代币的出殡和埋葬也是区块的劳动者,由此,嘉奖也归他具备。

让大家来证实一下表彰机制:

$ java -jar blockchain-java-jar-with-dependencies.jar createwallet wallet address : 1MpdtjTEsDvrkrLWmMswq4K3VPtevXXnUD$ java -jar blockchain-java-jar-with-dependencies.jar createwallet wallet address : 17crpQoWy7TEkY9UPjZ3Qt9Fc2rWPUt8KX$ java -jar blockchain-java-jar-with-dependencies.jar createwallet wallet address : 12L868QZW1ySYzf2oT5ha9py9M5JrSRhvT$ java -jar blockchain-java-jar-with-dependencies.jar createblockchain -address 1MpdtjTEsDvrkrLWmMswq4K3VPtevXXnUDElapsed Time: 17.973 secondscorrect hash Hex: 0000defe83a851a5db3803d5013bbc20c6234f176b2c52ae36fdb53d28b33d93 Done ! $ java -jar blockchain-java-jar-with-dependencies.jar send -from 1MpdtjTEsDvrkrLWmMswq4K3VPtevXXnUD -to 17crpQoWy7TEkY9UPjZ3Qt9Fc2rWPUt8KX -amount 6Elapsed Time: 30.887 secondscorrect hash Hex: 00005fd36a2609b43fd940577f93b8622e88e854f5ccfd70e113f763b6df69f7 Success!$ java -jar blockchain-java-jar-with-dependencies.jar send -from 1MpdtjTEsDvrkrLWmMswq4K3VPtevXXnUD -to 12L868QZW1ySYzf2oT5ha9py9M5JrSRhvT -amount 3Elapsed Time: 45.267 secondscorrect hash Hex: 00009fd7c59b830b60ec21ade7672921d2fb0962a1b06a42c245450e47582a13 Success!$ java -jar blockchain-java-jar-with-dependencies.jar getbalance -address 1MpdtjTEsDvrkrLWmMswq4K3VPtevXXnUDBalance of '1MpdtjTEsDvrkrLWmMswq4K3VPtevXXnUD': 21$ java -jar blockchain-java-jar-with-dependencies.jar getbalance -address 17crpQoWy7TEkY9UPjZ3Qt9Fc2rWPUt8KXBalance of '17crpQoWy7TEkY9UPjZ3Qt9Fc2rWPUt8KX': 6$ java -jar blockchain-java-jar-with-dependencies.jar getbalance -address 12L868QZW1ySYzf2oT5ha9py9M5JrSRhvTBalance of '12L868QZW1ySYzf2oT5ha9py9M5JrSRhvT': 3

1MpdtjTEsDvrkrLWmMswq4K3VPtevXXnUD 这么些地址一共接受了三份表彰:

  • 第二遍是开拓创世区块;

  • 其次次是开拓区块:00005fd36a2609b43fd940577f93b8622e88e854f5ccfd70e113f763b6df69f7

  • 其三回是开垦区块:00009fd7c59b830b60ec21ade7672921d2fb0962a1b06a42c245450e47582a13

Merkle Tree 是那篇小说中大家供给器重批评的叁个建制。

正如本身前面提到的那样,整个比特币的数据库占到了大致140G的磁盘空间。由于比特币的布满式天性,互连网中的每二个节点必得是单身且自给自足的。各样比特币节点都以路由、区块链数据库、挖矿、卡包服务的效果与利益集聚。每个节点都踏足全互联网的路由作用,相同的时间也或者包罗其余功效。种种节点都踏足验证并传到交易及区块音讯,开掘并保险与对等节点的接二连三。一个全节点(full node)满含以下多少个作用:

[图片上传失利...(image-d2ccef-1523845374124)]

乘机越来越多的人初步应用比特币,那条准则伊始变得越来越难以遵照:让每一人都去运作贰个一体化的节点不太现实。在中本聪公布的 比特币白皮书 中,针对这几个难点提议了一个缓慢解决方案:Simplified Payment Verification 。SPV是比特币的轻量级节点,它不供给下载全数的区块链数据,也不供给验证区块和交易数据。相反,当SPV想要验证一笔交易的可行时,它会从它所连接的全节点上搜求所急需的局地数据。这种机制确定保障了在唯有三个全节点的情况,能够运转多个SPV轻型卡车包节点。

更多关于SPV的介绍,请查看:《明白比特币》第八章

为了使SPV成为只怕,就供给有一种艺术在平素不全量下载区块数据的景观下,来检查三个区块是不是含有了某笔交易。那正是Merkle Tree 发挥效率的地点了。

比特币中所使用的Merkle Tree是为了博取贸易的Hash值,随后那些已经被Pow系统认同了的Hash值会被封存到区块头中。到近期甘休,大家只是简短地质测量算了贰个区块中每笔交易的Hash值,然后在希图Pow数据时,再对这一个交易举办 SHA-256 计算。就算那是一个用以获取区块交易独一代表的三个不错的不二等秘书诀,可是它不具备到 Merkle Tree的独到之处。

来看一下Merkle Tree的布局:

图片 2image

每叁个区块都会营造三个Merkle Tree,它从最尾巴部分的卡牌节点早先往上营造,每几个交易的Hash便是三个叶子节点(比特币中用的双SHA256算法)。叶子节点的数目必得是偶数个,可是实际不是每二个区块都能包括偶数笔交易数额。假若存在奇数笔交易数据,那么最终一笔交易数额将会被复制一份(那无非发生在Merkle Tree中,并不是区块中)。

从下往上运动,叶子节点成对分组,它们的Hash值被接连到联合,并且在此基础上再也总计出新的Hash值。新的Hash 变成新的树节点。这么些历程不断地被重复,直到最后仅剩二个被叫做根节点的树节点。这些根节点的Hash便是区块中贸易数额们的独一代表,它会被保留到区块头中,并被用来参加POW系统的测算。

Merkle树的补益是节点能够在不下载整个块的气象下验证某笔交易的合法性。 为此,只须要交易Hash,Merkle树根Hash和Merkle路线。

Merkle Tree代码完毕如下:

package one.wangwei.blockchain.transaction;import com.google.common.collect.Lists;import lombok.Data;import one.wangwei.blockchain.util.ByteUtils;import org.apache.commons.codec.digest.DigestUtils;import java.util.List;/** * 默克尔树 * * @author wangwei * @date 2018/04/15 */@Datapublic class MerkleTree { /** * 根节点 */ private Node root; /** * 叶子节点Hash */ private byte[][] leafHashes; public MerkleTree(byte[][] leafHashes) { constructTree(leafHashes); } /** * 从底部叶子节点开始往上构建整个Merkle Tree * * @param leafHashes */ private void constructTree(byte[][] leafHashes) { if (leafHashes == null || leafHashes.length < 1) { throw new RuntimeException("ERROR:Fail to construct merkle tree ! leafHashes data invalid ! "); } this.leafHashes = leafHashes; List<Node> parents = bottomLevel(leafHashes); while (parents.size { parents = internalLevel; } root = parents.get; } /** * 构建一个层级节点 * * @param children * @return */ private List<Node> internalLevel(List<Node> children) { List<Node> parents = Lists.newArrayListWithCapacity(children.size; for (int i = 0; i < children.size() - 1; i += 2) { Node child1 = children.get; Node child2 = children.get; Node parent = constructInternalNode(child1, child2); parents.add; } // 内部节点奇数个,只对left节点进行计算 if (children.size() % 2 != 0) { Node child = children.get(children.size; Node parent = constructInternalNode(child, null); parents.add; } return parents; } /** * 底部节点构建 * * @param hashes * @return */ private List<Node> bottomLevel(byte[][] hashes) { List<Node> parents = Lists.newArrayListWithCapacity(hashes.length / 2); for (int i = 0; i < hashes.length - 1; i += 2) { Node leaf1 = constructLeafNode(hashes[i]); Node leaf2 = constructLeafNode(hashes[i + 1]); Node parent = constructInternalNode(leaf1, leaf2); parents.add; } if (hashes.length % 2 != 0) { Node leaf = constructLeafNode(hashes[hashes.length - 1]); // 奇数个节点的情况,复制最后一个节点 Node parent = constructInternalNode(leaf, leaf); parents.add; } return parents; } /** * 构建叶子节点 * * @param hash * @return */ private static Node constructLeafNode(byte[] hash) { Node leaf = new Node(); leaf.hash = hash; return leaf; } /** * 构建内部节点 * * @param leftChild * @param rightChild * @return */ private Node constructInternalNode(Node leftChild, Node rightChild) { Node parent = new Node(); if (rightChild == null) { parent.hash = leftChild.hash; } else { parent.hash = internalHash(leftChild.hash, rightChild.hash); } parent.left = leftChild; parent.right = rightChild; return parent; } /** * 计算内部节点Hash * * @param leftChildHash * @param rightChildHash * @return */ private byte[] internalHash(byte[] leftChildHash, byte[] rightChildHash) { byte[] mergedBytes = ByteUtils.merge(leftChildHash, rightChildHash); return DigestUtils.sha256(mergedBytes); } /** * Merkle Tree节点 */ @Data public static class Node { private byte[] hash; private Node left; private Node right; }}

然后修改 Block.hashTransaction 接口:

public class Block { ... /** * 对区块中的交易信息进行Hash计算 * * @return */ public byte[] hashTransaction() { byte[][] txIdArrays = new byte[this.getTransactions().length][]; for (int i = 0; i < this.getTransactions().length; i++) { txIdArrays[i] = this.getTransactions()[i].hash(); } return new MerkleTree(txIdArrays).getRoot().getHash(); } ... }

MerkleTree的根节点的Hash值,正是区块中贸易新闻的独一象征。

这一节大家器重是对前边的贸易机制做了一发的优化,参与UTXO池和Merkle Tree机制。

  1. 源码:
  2. The UTXO Set
  3. UTXO set statistics
  4. Merkle Tree
  5. Why every Bitcoin user should understand “SPV security”
  6. Script
  7. “Ultraprune” Bitcoin Core commit
  8. Smart contracts and Bitcoin

图片 3image

编辑:118最快开奖现场直播 本文来源:还要集中于"数据存款和储蓄"这一环节

关键词: