mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-24 15:02:17 +01:00
Merge pull request #26 from Christewart/merkle_tree_type
Adding function inside of Merkle to actually build and return the Mer…
This commit is contained in:
commit
37eb7f2d03
2 changed files with 47 additions and 36 deletions
|
@ -3,7 +3,7 @@ package org.bitcoins.core.consensus
|
|||
import org.bitcoins.core.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.core.protocol.blockchain.Block
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, CryptoUtil}
|
||||
import org.bitcoins.core.util._
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
|
@ -14,6 +14,8 @@ import scala.annotation.tailrec
|
|||
* https://github.com/bitcoin/bitcoin/blob/master/src/consensus/merkle.cpp
|
||||
*/
|
||||
trait Merkle extends BitcoinSLogger {
|
||||
|
||||
type MerkleTree = BinaryTree[DoubleSha256Digest]
|
||||
/**
|
||||
* Computes the merkle root for the given block
|
||||
* @param block the given block that needs the merkle root computed
|
||||
|
@ -21,34 +23,6 @@ trait Merkle extends BitcoinSLogger {
|
|||
*/
|
||||
def computeBlockMerkleRoot(block : Block) : DoubleSha256Digest = computeMerkleRoot(block.transactions)
|
||||
|
||||
/**
|
||||
* Computes the merkle root of a given sequence of hashes
|
||||
* This hash function assumes that all of the hashes are big endian encoded
|
||||
* @param hashes the big endian encoded hashes from which the merkle root is derived from
|
||||
* @param accum the accumulator for the recursive call on this function - more than likely you do not need to worry about this
|
||||
* @return the merkle root
|
||||
*/
|
||||
@tailrec
|
||||
final def computeMerkleRoot(hashes : Seq[DoubleSha256Digest], accum : List[DoubleSha256Digest] = List()) : DoubleSha256Digest = hashes match {
|
||||
case Nil =>
|
||||
logger.debug("No more hashes to check")
|
||||
if (accum.size == 1) DoubleSha256Digest(accum.head.bytes.reverse)
|
||||
else if (accum.size == 0) throw new RuntimeException("We cannot have zero hashes and nothing in the accumulator, this means there is NO transaction in the block. " +
|
||||
"There always should be ATLEAST one - the coinbase tx")
|
||||
else computeMerkleRoot(accum.reverse, List())
|
||||
case h :: h1 :: t =>
|
||||
logger.debug("We have an even amount of txids")
|
||||
logger.debug("Hashes: " + hashes.map(_.hex))
|
||||
val hash = CryptoUtil.doubleSHA256(h.bytes ++ h1.bytes)
|
||||
computeMerkleRoot(t, hash :: accum)
|
||||
case h :: t =>
|
||||
logger.debug("We have an odd amount of txids")
|
||||
logger.debug("Hashes: " + hashes.map(_.hex))
|
||||
//means that we have an odd amount of txids, this means we duplicate the last hash in the tree
|
||||
val hash = CryptoUtil.doubleSHA256(h.bytes ++ h.bytes)
|
||||
computeMerkleRoot(t,hash :: accum)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the merkle root for the given sequence of transactions
|
||||
* @param transactions the list of transactions whose merkle root needs to be computed
|
||||
|
@ -58,8 +32,45 @@ trait Merkle extends BitcoinSLogger {
|
|||
case Nil => throw new IllegalArgumentException("We cannot have zero transactions in the block. There always should be ATLEAST one - the coinbase tx")
|
||||
case h :: Nil => h.txId
|
||||
case h :: t =>
|
||||
val txHashes = transactions.map(tx => tx.txId)
|
||||
computeMerkleRoot(txHashes)
|
||||
val leafs = transactions.map(tx => Leaf(tx.txId))
|
||||
val merkleTree = build(leafs,Nil)
|
||||
merkleTree.value.get
|
||||
}
|
||||
|
||||
/** Builds a [[MerkleTree]] from sequence of sub merkle trees.
|
||||
* This subTrees can be individual txids (leafs) or full blown subtrees
|
||||
* @param subTrees the trees that need to be hashed
|
||||
* @param accum the accumulated merkle trees, waiting to be hashed next round
|
||||
* @return the entire Merkle tree computed from the given merkle trees
|
||||
*/
|
||||
@tailrec
|
||||
final def build(subTrees: Seq[MerkleTree], accum: Seq[MerkleTree]): MerkleTree = subTrees match {
|
||||
case Nil =>
|
||||
if (accum.size == 1) accum.head
|
||||
else if (accum.isEmpty) throw new IllegalArgumentException("Should never have sub tree size of zero, this implies there was zero hashes given")
|
||||
else build(accum.reverse, Nil)
|
||||
case h :: h1 :: t =>
|
||||
logger.debug("Subtrees: " + subTrees)
|
||||
val newTree = computeTree(h,h1)
|
||||
build(t, newTree +: accum)
|
||||
case h :: t =>
|
||||
logger.debug("Subtrees: " + subTrees)
|
||||
//means that we have an odd amount of txids, this means we duplicate the last hash in the tree
|
||||
val newTree = computeTree(h,h)
|
||||
build(t, newTree +: accum)
|
||||
}
|
||||
|
||||
/** Builds a merkle tree from a sequence of hashes */
|
||||
def build(hashes: Seq[DoubleSha256Digest]): MerkleTree = {
|
||||
val leafs = hashes.map(Leaf(_))
|
||||
build(leafs,Nil)
|
||||
}
|
||||
|
||||
/** Computes the merkle tree of two sub merkle trees */
|
||||
def computeTree(tree1: MerkleTree, tree2: MerkleTree): MerkleTree = {
|
||||
val bytes = tree1.value.get.bytes ++ tree2.value.get.bytes
|
||||
val hash = CryptoUtil.doubleSHA256(bytes)
|
||||
Node(hash,tree1,tree2)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ class MerkleTest extends FlatSpec with MustMatchers {
|
|||
require(tx1.txId.hex == BitcoinSUtil.flipEndianess("5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2"))
|
||||
|
||||
val transactions = Seq(coinbaseTx,tx1)
|
||||
Merkle.computeMerkleRoot(transactions).hex must be ("8fb300e3fdb6f30a4c67233b997f99fdd518b968b9a3fd65857bfe78b2600719")
|
||||
Merkle.computeMerkleRoot(transactions).hex must be (BitcoinSUtil.flipEndianess("8fb300e3fdb6f30a4c67233b997f99fdd518b968b9a3fd65857bfe78b2600719"))
|
||||
}
|
||||
|
||||
it must "correctly compute the merkle root for a block with 3 transactions" in {
|
||||
|
@ -37,7 +37,7 @@ class MerkleTest extends FlatSpec with MustMatchers {
|
|||
require(tx2.txId.hex == BitcoinSUtil.flipEndianess("d8c9d6a13a7fb8236833b1e93d298f4626deeb78b2f1814aa9a779961c08ce39"))
|
||||
val transactions = Seq(coinbaseTx, tx1, tx2)
|
||||
|
||||
Merkle.computeMerkleRoot(transactions).hex must be ("d277b5d20fab7cdb8140ab953323b585445d4920ad7226623d9c7ed0bc6b9a57")
|
||||
Merkle.computeMerkleRoot(transactions).hex must be (BitcoinSUtil.flipEndianess("d277b5d20fab7cdb8140ab953323b585445d4920ad7226623d9c7ed0bc6b9a57"))
|
||||
}
|
||||
|
||||
it must "correctly compute the merkle root for the 100,000 block which has 4 transactions" in {
|
||||
|
@ -50,7 +50,7 @@ class MerkleTest extends FlatSpec with MustMatchers {
|
|||
|
||||
val transactions = Seq(coinbaseTx, tx1,tx2,tx3)
|
||||
|
||||
Merkle.computeMerkleRoot(transactions).hex must be ("f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766")
|
||||
Merkle.computeMerkleRoot(transactions).hex must be (BitcoinSUtil.flipEndianess("f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766"))
|
||||
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ class MerkleTest extends FlatSpec with MustMatchers {
|
|||
require(tx4.txId.hex == BitcoinSUtil.flipEndianess("b79606d7377285e1a4f9cb757c9f1a1d36f09c10bc52be49b170c6dd40767669"))
|
||||
|
||||
val transactions = Seq(coinbaseTx,tx1,tx2,tx3,tx4)
|
||||
Merkle.computeMerkleRoot(transactions).hex must be ("36b38854f9adf76b4646ab2c0f949846408cfab2c045f110d01f84f4122c5add")
|
||||
Merkle.computeMerkleRoot(transactions).hex must be (BitcoinSUtil.flipEndianess("36b38854f9adf76b4646ab2c0f949846408cfab2c045f110d01f84f4122c5add"))
|
||||
}
|
||||
|
||||
it must "correctly compute the merkle root for a block with 11 transactions" in {
|
||||
|
@ -98,6 +98,6 @@ class MerkleTest extends FlatSpec with MustMatchers {
|
|||
|
||||
val transactions = Seq(coinbaseTx,tx1,tx2,tx3,tx4,tx5,tx6,tx7,tx8,tx9,tx10)
|
||||
|
||||
Merkle.computeMerkleRoot(transactions).hex must be ("2c8230dfb6c7ad0949cbdb7d3c3616cc10770db7e832bbf5c9b261388828c6c4")
|
||||
Merkle.computeMerkleRoot(transactions).hex must be (BitcoinSUtil.flipEndianess("2c8230dfb6c7ad0949cbdb7d3c3616cc10770db7e832bbf5c9b261388828c6c4"))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue