mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-18 21:34:39 +01:00
Implemented BIP 157 Block Filter Headers (#532)
This commit is contained in:
parent
a87858247c
commit
67fb821b00
@ -1,6 +1,6 @@
|
||||
package org.bitcoins.core.gcs
|
||||
|
||||
import org.bitcoins.core.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
||||
import org.bitcoins.core.protocol.blockchain.Block
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||
@ -14,20 +14,28 @@ class BlockFilterTest extends BitcoinSUnitTest {
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki#appendix-c-test-vectors
|
||||
case class Bip158TestCase(
|
||||
blockHeight: Int,
|
||||
blockHash: DoubleSha256Digest,
|
||||
blockHash: DoubleSha256DigestBE,
|
||||
block: Block,
|
||||
prevOutputScripts: Vector[ScriptPubKey],
|
||||
// TODO prevHeader: BlockFilterHeader,
|
||||
prevHeader: DoubleSha256DigestBE,
|
||||
filter: GolombFilter,
|
||||
// TODO header: BlockFilterHeader,
|
||||
header: DoubleSha256DigestBE,
|
||||
notes: String
|
||||
) {
|
||||
|
||||
val clue: String = s"Test Notes: $notes"
|
||||
|
||||
def runTest(): org.scalatest.Assertion = {
|
||||
val constructedFilter = BlockFilter(block, prevOutputScripts)
|
||||
|
||||
assert(constructedFilter.decodedHashes == filter.decodedHashes,
|
||||
s"Test Notes: $notes")
|
||||
assert(constructedFilter.decodedHashes == filter.decodedHashes, clue)
|
||||
|
||||
assert(constructedFilter.encodedData.bytes == filter.encodedData.bytes,
|
||||
clue)
|
||||
|
||||
val constructedHeader = constructedFilter.getHeader(prevHeader.flip)
|
||||
|
||||
assert(constructedHeader.hash == header.flip, clue)
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,23 +45,33 @@ class BlockFilterTest extends BitcoinSUnitTest {
|
||||
def fromJsArray(array: JsArray): Bip158TestCase = {
|
||||
val parseResult = for {
|
||||
height <- array(0).validate[Int]
|
||||
blockHash <- array(1).validate[String].map(DoubleSha256Digest.fromHex)
|
||||
blockHash <- array(1).validate[String].map(DoubleSha256DigestBE.fromHex)
|
||||
|
||||
block <- array(2).validate[String].map(Block.fromHex)
|
||||
|
||||
scriptArray <- array(3).validate[JsArray]
|
||||
scripts = parseScripts(scriptArray)
|
||||
|
||||
//prevHeader <- array(4).validate[String].map(BlockFilterHeader.fromHex)
|
||||
prevHeader <- array(4)
|
||||
.validate[String]
|
||||
.map(DoubleSha256DigestBE.fromHex)
|
||||
|
||||
filter <- array(5)
|
||||
.validate[String]
|
||||
.map(BlockFilter.fromHex(_, blockHash))
|
||||
.map(BlockFilter.fromHex(_, blockHash.flip))
|
||||
|
||||
//header <- array(6).validate[String].map(BlockFilterHeader.fromHex)
|
||||
header <- array(6).validate[String].map(DoubleSha256DigestBE.fromHex)
|
||||
|
||||
notes <- array(7).validate[String]
|
||||
} yield Bip158TestCase(height, blockHash, block, scripts, filter, notes)
|
||||
} yield
|
||||
Bip158TestCase(height,
|
||||
blockHash,
|
||||
block,
|
||||
scripts,
|
||||
prevHeader,
|
||||
filter,
|
||||
header,
|
||||
notes)
|
||||
|
||||
parseResult.get
|
||||
}
|
||||
|
28
core/src/main/scala/org/bitcoins/core/gcs/FilterHeader.scala
Normal file
28
core/src/main/scala/org/bitcoins/core/gcs/FilterHeader.scala
Normal file
@ -0,0 +1,28 @@
|
||||
package org.bitcoins.core.gcs
|
||||
|
||||
import org.bitcoins.core.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.core.util.CryptoUtil
|
||||
|
||||
/**
|
||||
* Bip 157 Block Filter Headers which commit to a chain of block filters,
|
||||
* much in the same way that block headers commit to a block chain
|
||||
* @see [[https://github.com/bitcoin/bips/blob/master/bip-0157.mediawiki#filter-headers]]
|
||||
*/
|
||||
case class FilterHeader(
|
||||
filterHash: DoubleSha256Digest,
|
||||
prevHeaderHash: DoubleSha256Digest) {
|
||||
|
||||
val hash: DoubleSha256Digest = {
|
||||
CryptoUtil.doubleSHA256(filterHash.bytes ++ prevHeaderHash.bytes)
|
||||
}
|
||||
|
||||
/** Given the next Block Filter, constructs the next Block Filter Header */
|
||||
def nextHeader(nextFilter: GolombFilter): FilterHeader = {
|
||||
FilterHeader(filterHash = nextFilter.hash, prevHeaderHash = this.hash)
|
||||
}
|
||||
|
||||
/** Given the next Block Filter hash, constructs the next Block Filter Header */
|
||||
def nextHeader(nextFilterHash: DoubleSha256Digest): FilterHeader = {
|
||||
FilterHeader(filterHash = nextFilterHash, prevHeaderHash = this.hash)
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ import org.bitcoins.core.protocol.transaction.{
|
||||
TransactionOutput
|
||||
}
|
||||
import org.bitcoins.core.script.control.OP_RETURN
|
||||
import org.bitcoins.core.util.BitcoinSUtil
|
||||
import org.bitcoins.core.util.{BitcoinSUtil, CryptoUtil}
|
||||
import scodec.bits.{BitVector, ByteVector}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
@ -31,6 +31,27 @@ case class GolombFilter(
|
||||
encodedData: BitVector) {
|
||||
lazy val decodedHashes: Vector[UInt64] = GCS.golombDecodeSet(encodedData, p)
|
||||
|
||||
/** The hash of this serialized filter */
|
||||
lazy val hash: DoubleSha256Digest = {
|
||||
CryptoUtil.doubleSHA256(this.bytes)
|
||||
}
|
||||
|
||||
/** Given the previous FilterHeader, constructs the header corresponding to this */
|
||||
def getHeader(prevHeader: FilterHeader): FilterHeader = {
|
||||
FilterHeader(filterHash = this.hash, prevHeaderHash = prevHeader.hash)
|
||||
}
|
||||
|
||||
/** Given the previous FilterHeader hash, constructs the header corresponding to this */
|
||||
def getHeader(prevHeaderHash: DoubleSha256Digest): FilterHeader = {
|
||||
FilterHeader(filterHash = this.hash, prevHeaderHash = prevHeaderHash)
|
||||
}
|
||||
|
||||
def bytes: ByteVector = {
|
||||
n.bytes ++ encodedData.bytes
|
||||
}
|
||||
|
||||
def hex: String = bytes.toHex
|
||||
|
||||
// TODO: Offer alternative that stops decoding when it finds out if data is there
|
||||
def matchesHash(hash: UInt64): Boolean = {
|
||||
@tailrec
|
||||
|
Loading…
Reference in New Issue
Block a user