mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-20 10:13:26 +01:00
Adding function inside of Merkle to actually build and return the MerkleTree, changing how endianess is stored inside of the Merkle root from big endian to little endian
This commit is contained in:
parent
8003ea1d18
commit
ed37b610d2
@ -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,40 @@ 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
|
||||
}
|
||||
|
||||
@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("Computing the merkle tree for two sub merkle trees")
|
||||
logger.debug("Subtrees: " + subTrees)
|
||||
val newTree = computeTree(h,h1)
|
||||
logger.debug("newTree: " + newTree)
|
||||
logger.debug("new subtree seq: " + t)
|
||||
build(t, newTree +: accum)
|
||||
case h :: t =>
|
||||
logger.debug("Computing the merkle tree for one sub merkle tree - this means duplicating the subtree")
|
||||
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)
|
||||
logger.debug("newTree: " + newTree)
|
||||
logger.debug("new subtree seq: " + t)
|
||||
build(t, newTree +: accum)
|
||||
}
|
||||
|
||||
|
||||
/** 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…
Reference in New Issue
Block a user