diff --git a/README.md b/README.md index f0e5fabc0..d6fd4b7d8 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ All contributions are welcome. Please see [[this page](https://github.com/Bit-Wa # Other projects + - [bitcoin-php-bloom-filter](https://github.com/Bit-Wasp/bitcoin-php-bloom-filter) - Package providing bloom filter support. - [buffertools-php](https://github.com/Bit-Wasp/buffertools-php) - Toolkit for working with binary data in PHP - [ext-secp256k1](https://github.com/Bit-Wasp/secp256k1-php) - PHP bindings to libsecp256k1 - [ext-bitcoinconsensus](https://github.com/Bit-Wasp/bitcoinconsensus-php) - PHP bindings to libbitcoinconsensus diff --git a/src/Block/Block.php b/src/Block/Block.php index b952b1068..d2ffdf936 100644 --- a/src/Block/Block.php +++ b/src/Block/Block.php @@ -4,7 +4,6 @@ namespace BitWasp\Bitcoin\Block; -use BitWasp\Bitcoin\Bloom\BloomFilter; use BitWasp\Bitcoin\Math\Math; use BitWasp\Bitcoin\Serializable; use BitWasp\Bitcoin\Serializer\Block\BlockHeaderSerializer; @@ -94,25 +93,6 @@ public function getTransaction(int $i): TransactionInterface return $this->transactions[$i]; } - /** - * @param BloomFilter $filter - * @return FilteredBlock - */ - public function filter(BloomFilter $filter): FilteredBlock - { - $vMatch = []; - $vHashes = []; - foreach ($this->getTransactions() as $tx) { - $vMatch[] = $filter->isRelevantAndUpdate($tx); - $vHashes[] = $tx->getTxHash(); - } - - return new FilteredBlock( - $this->getHeader(), - PartialMerkleTree::create(count($this->getTransactions()), $vHashes, $vMatch) - ); - } - /** * {@inheritdoc} * @see \BitWasp\Buffertools\SerializableInterface::getBuffer() diff --git a/src/Block/BlockInterface.php b/src/Block/BlockInterface.php index c3808aae1..65c9b4d7f 100644 --- a/src/Block/BlockInterface.php +++ b/src/Block/BlockInterface.php @@ -4,7 +4,6 @@ namespace BitWasp\Bitcoin\Block; -use BitWasp\Bitcoin\Bloom\BloomFilter; use BitWasp\Bitcoin\SerializableInterface; use BitWasp\Bitcoin\Transaction\TransactionInterface; use BitWasp\Buffertools\BufferInterface; @@ -39,10 +38,4 @@ public function getTransactions(): array; * @return TransactionInterface */ public function getTransaction(int $i): TransactionInterface; - - /** - * @param BloomFilter $filter - * @return FilteredBlock - */ - public function filter(BloomFilter $filter): FilteredBlock; } diff --git a/src/Block/FilteredBlock.php b/src/Block/FilteredBlock.php deleted file mode 100644 index 247f21662..000000000 --- a/src/Block/FilteredBlock.php +++ /dev/null @@ -1,58 +0,0 @@ -header = $header; - $this->partialTree = $merkleTree; - } - - /** - * @return BlockHeaderInterface - */ - public function getHeader(): BlockHeaderInterface - { - return $this->header; - } - - /** - * @return PartialMerkleTree - */ - public function getPartialTree(): PartialMerkleTree - { - return $this->partialTree; - } - - /** - * @return BufferInterface - */ - public function getBuffer(): BufferInterface - { - return (new FilteredBlockSerializer(new BlockHeaderSerializer(), new PartialMerkleTreeSerializer()))->serialize($this); - } -} diff --git a/src/Block/PartialMerkleTree.php b/src/Block/PartialMerkleTree.php deleted file mode 100644 index 9b74429fb..000000000 --- a/src/Block/PartialMerkleTree.php +++ /dev/null @@ -1,262 +0,0 @@ -elementCount = $txCount; - $this->vHashes = $vHashes; - $this->vFlagBits = $vBits; - } - - /** - * Construct the Merkle tree - * - * @param int $txCount - * @param array $vTxHashes - * @param array $vMatch - * @return PartialMerkleTree - */ - public static function create(int $txCount, array $vTxHashes, array $vMatch) - { - $tree = new self($txCount); - $tree->traverseAndBuild($tree->calcTreeHeight(), 0, $vTxHashes, $vMatch); - return $tree; - } - - /** - * Calculate tree width for a given height. - * - * @param int $height - * @return int - */ - public function calcTreeWidth(int $height) - { - return ($this->elementCount + (1 << $height) - 1) >> $height; - } - - /** - * Calculate the tree height. - * - * @return int - */ - public function calcTreeHeight(): int - { - $height = 0; - while ($this->calcTreeWidth($height) > 1) { - $height++; - } - - return $height; - } - - /** - * @return int - */ - public function getTxCount(): int - { - return $this->elementCount; - } - - /** - * @return BufferInterface[] - */ - public function getHashes(): array - { - return $this->vHashes; - } - - /** - * @return array - */ - public function getFlagBits(): array - { - return $this->vFlagBits; - } - - /** - * Calculate the hash for the given $height and $position - * - * @param int $height - * @param int $position - * @param \BitWasp\Buffertools\BufferInterface[] $vTxid - * @return \BitWasp\Buffertools\BufferInterface - */ - public function calculateHash(int $height, $position, array $vTxid): BufferInterface - { - if ($height === 0) { - return $vTxid[$position]; - } else { - $left = $this->calculateHash($height - 1, $position * 2, $vTxid); - if (($position * 2 + 1) < $this->calcTreeWidth($height - 1)) { - $right = $this->calculateHash($height - 1, ($position * 2 + 1), $vTxid); - } else { - $right = $left; - } - - return Hash::sha256d(Buffertools::concat($left, $right)); - } - } - - /** - * Construct the list of Merkle Tree hashes - * - * @param int $height - * @param int $position - * @param array $vTxid - array of Txid's in the block - * @param array $vMatch - reference to array to populate - */ - public function traverseAndBuild(int $height, int $position, array $vTxid, array &$vMatch) - { - $parent = false; - for ($p = $position << $height; $p < ($position + 1) << $height && $p < $this->elementCount; $p++) { - $parent |= $vMatch[$p]; - } - - $this->vFlagBits[] = $parent; - - if (0 === $height || !$parent) { - $this->vHashes[] = $this->calculateHash($height, $position, $vTxid); - } else { - $this->traverseAndBuild($height - 1, $position * 2, $vTxid, $vMatch); - if (($position * 2 + 1) < $this->calcTreeWidth($height - 1)) { - $this->traverseAndBuild($height - 1, $position * 2 + 1, $vTxid, $vMatch); - } - } - } - - /** - * Traverse the Merkle Tree hashes and extract those which have a matching bit. - * - * @param int $height - * @param int $position - * @param int $nBitsUsed - * @param int $nHashUsed - * @param BufferInterface[] $vMatch - * @return BufferInterface - */ - public function traverseAndExtract(int $height, int $position, &$nBitsUsed, &$nHashUsed, &$vMatch): BufferInterface - { - if ($nBitsUsed >= count($this->vFlagBits)) { - $this->fBad = true; - return new Buffer(); - } - - $parent = $this->vFlagBits[$nBitsUsed++]; - if (0 === $height || !$parent) { - if ($nHashUsed >= count($this->vHashes)) { - $this->fBad = true; - return new Buffer(); - } - $hash = $this->vHashes[$nHashUsed++]; - if ($height === 0 && $parent) { - $vMatch[] = $hash->flip(); - } - return $hash; - } else { - $left = $this->traverseAndExtract($height - 1, $position * 2, $nBitsUsed, $nHashUsed, $vMatch); - if (($position * 2 + 1) < $this->calcTreeWidth($height - 1)) { - $right = $this->traverseAndExtract($height - 1, ($position * 2 + 1), $nBitsUsed, $nHashUsed, $vMatch); - if ($right === $left) { - $this->fBad = true; - } - } else { - $right = $left; - } - - return Hash::sha256d(Buffertools::concat($left, $right)); - } - } - - /** - * Extract matches from the tree into provided $vMatch reference. - * - * @param BufferInterface[] $vMatch - reference to array of extracted 'matching' hashes - * @return BufferInterface - this will be the merkle root - * @throws \Exception - */ - public function extractMatches(array &$vMatch): BufferInterface - { - $nTx = $this->getTxCount(); - if (0 === $nTx) { - throw new \Exception('ntx = 0'); - } - - if ($nTx > BlockInterface::MAX_BLOCK_SIZE / 60) { - throw new \Exception('ntx > bound size'); - } - - if (count($this->vHashes) > $nTx) { - throw new \Exception('nHashes > nTx'); - } - - if (count($this->vFlagBits) < count($this->vHashes)) { - throw new \Exception('nBits < nHashes'); - } - - $height = $this->calcTreeHeight(); - $nBitsUsed = 0; - $nHashesUsed = 0; - $merkleRoot = $this->traverseAndExtract($height, 0, $nBitsUsed, $nHashesUsed, $vMatch); - $merkleRoot = $merkleRoot->flip(); - if ($this->fBad) { - throw new \Exception('bad data'); - } - - if (ceil(($nBitsUsed + 7) / 8) !== ceil((count($this->vFlagBits)+7)/8)) { - throw new \Exception('Not all bits consumed'); - } - - if ($nHashesUsed !== count($this->vHashes)) { - throw new \Exception('Not all hashes consumed'); - } - - return $merkleRoot; - } - - /** - * @return BufferInterface - */ - public function getBuffer(): BufferInterface - { - return (new PartialMerkleTreeSerializer())->serialize($this); - } -} diff --git a/src/Bloom/BloomFilter.php b/src/Bloom/BloomFilter.php deleted file mode 100644 index 9c650e675..000000000 --- a/src/Bloom/BloomFilter.php +++ /dev/null @@ -1,412 +0,0 @@ -math = $math; - $this->vFilter = $vFilter; - $this->numHashFuncs = $numHashFuncs; - $this->nTweak = $nTweak; - $this->flags = $flags; - $this->updateEmptyFull(); - } - - /** - * @param int $size - * @return array - */ - public static function emptyFilter(int $size): array - { - return str_split(str_pad('', $size, '0'), 1); - } - - /** - * Create the Bloom Filter given the number of elements, a false positive rate, - * and the flags governing how the filter should be updated. - * - * @param Math $math - * @param int $nElements - * @param float $nFpRate - * @param int $nTweak - * @param int $flags - * @return BloomFilter - */ - public static function create(Math $math, int $nElements, float $nFpRate, int $nTweak, int $flags): BloomFilter - { - $size = self::idealSize($nElements, $nFpRate); - - return new self( - $math, - self::emptyFilter($size), - self::idealNumHashFuncs($size, $nElements), - $nTweak, - $flags - ); - } - - /** - * @return bool - */ - public function isUpdateNone(): bool - { - return (($this->flags & self::UPDATE_MASK) === self::UPDATE_NONE); - } - - /** - * @return bool - */ - public function isUpdateAll(): bool - { - return (($this->flags & self::UPDATE_MASK) === self::UPDATE_ALL); - } - - /** - * @return bool - */ - public function isUpdatePubKeyOnly(): bool - { - return (($this->flags & self::UPDATE_MASK) === self::UPDATE_P2PUBKEY_ONLY); - } - - /** - * @return bool - */ - public function isEmpty(): bool - { - return $this->empty; - } - - /** - * @return bool - */ - public function isFull(): bool - { - return $this->full; - } - - /** - * @return array - */ - public function getData(): array - { - return $this->vFilter; - } - - /** - * @return int - */ - public function getNumHashFuncs(): int - { - return $this->numHashFuncs; - } - - /** - * @return int - */ - public function getTweak(): int - { - return $this->nTweak; - } - - /** - * @return int - */ - public function getFlags(): int - { - return $this->flags; - } - - /** - * @param int $nElements - * @param float $fpRate - * @return int - */ - public static function idealSize(int $nElements, float $fpRate): int - { - return (int) floor( - bcdiv( - min( - bcmul( - bcmul( - bcdiv( - '-1', - (string) self::LN2SQUARED - ), - (string) $nElements - ), - (string) log($fpRate) - ), - bcmul( - (string) self::MAX_FILTER_SIZE, - '8' - ) - ), - '8' - ) - ); - } - - /** - * @param int $filterSize - * @param int $nElements - * @return int - */ - public static function idealNumHashFuncs(int $filterSize, int $nElements) - { - return (int) floor( - min( - bcmul( - bcdiv( - bcmul( - (string) $filterSize, - '8' - ), - (string) $nElements - ), - (string) self::LN2 - ), - bcmul( - (string) self::MAX_FILTER_SIZE, - '8' - ) - ) - ); - } - - /** - * @param int $nHashNum - * @param BufferInterface $data - * @return string - */ - public function hash(int $nHashNum, BufferInterface $data): string - { - $hash = Hash::murmur3($data, ($nHashNum * self::TWEAK_START + $this->nTweak) & 0xffffffff)->getInt(); - $hash = gmp_init($hash, 10); - $hash = $this->math->mod($hash, gmp_init(count($this->vFilter) * 8)); - return gmp_strval($hash, 10); - } - - /** - * @param BufferInterface $data - * @return $this - */ - public function insertData(BufferInterface $data) - { - if ($this->isFull()) { - return $this; - } - - for ($i = 0; $i < $this->numHashFuncs; $i++) { - $index = $this->hash($i, $data); - $this->vFilter[$index >> 3] |= (1 << (7 & $index)); - } - - $this->updateEmptyFull(); - return $this; - } - - /** - * @param OutPointInterface $outPoint - * @return BloomFilter - */ - public function insertOutPoint(OutPointInterface $outPoint): BloomFilter - { - return $this->insertData($outPoint->getBuffer()); - } - - /** - * @param BufferInterface $data - * @return bool - */ - public function containsData(BufferInterface $data): bool - { - if ($this->isFull()) { - return true; - } - - if ($this->isEmpty()) { - return false; - } - - for ($i = 0; $i < $this->numHashFuncs; $i++) { - $index = $this->hash($i, $data); - - if (!($this->vFilter[($index >> 3)] & (1 << (7 & $index)))) { - return false; - } - } - - return true; - } - - /** - * @param OutPointInterface $outPoint - * @return bool - */ - public function containsOutPoint(OutPointInterface $outPoint): bool - { - return $this->containsData($outPoint->getBuffer()); - } - - /** - * @return bool - */ - public function hasAcceptableSize(): bool - { - return count($this->vFilter) <= self::MAX_FILTER_SIZE && $this->numHashFuncs <= self::MAX_HASH_FUNCS; - } - - /** - * @param TransactionInterface $tx - * @return bool - */ - public function isRelevantAndUpdate(TransactionInterface $tx): bool - { - $this->updateEmptyFull(); - $found = false; - if ($this->isFull()) { - return true; - } - - if ($this->isEmpty()) { - return false; - } - - // Check if the txid hash is in the filter - $txHash = $tx->getTxId(); - if ($this->containsData($txHash)) { - $found = true; - } - - $classifier = new OutputClassifier(); - - // Check for relevant output scripts. We add the outpoint to the filter if found. - foreach ($tx->getOutputs() as $vout => $output) { - $script = $output->getScript(); - $parser = $script->getScriptParser(); - foreach ($parser as $exec) { - if ($exec->isPush() && $this->containsData($exec->getData())) { - $found = true; - if ($this->isUpdateAll()) { - $this->insertOutPoint($tx->makeOutPoint($vout)); - } else if ($this->isUpdatePubKeyOnly()) { - if ($classifier->isMultisig($script) || $classifier->isPayToPublicKey($script)) { - $this->insertOutPoint($tx->makeOutPoint($vout)); - } - } - } - } - } - - if ($found) { - return true; - } - - foreach ($tx->getInputs() as $txIn) { - if ($this->containsOutPoint($txIn->getOutPoint())) { - return true; - } - - $parser = $txIn->getScript()->getScriptParser(); - foreach ($parser as $exec) { - if ($exec->isPush() > 0 && $this->containsData($exec->getData())) { - return true; - } - } - } - - return false; - } - - /** - * - */ - public function updateEmptyFull() - { - $full = true; - $empty = true; - for ($i = 0, $size = count($this->vFilter); $i < $size; $i++) { - $byte = (int) $this->vFilter[$i]; - $full &= ($byte === 0xff); - $empty &= ($byte === 0x0); - } - - $this->full = (bool)$full; - $this->empty = (bool)$empty; - } - - /** - * @return BufferInterface - */ - public function getBuffer(): BufferInterface - { - return (new BloomFilterSerializer())->serialize($this); - } -} diff --git a/src/Serializer/Block/FilteredBlockSerializer.php b/src/Serializer/Block/FilteredBlockSerializer.php deleted file mode 100644 index 496fd1a7d..000000000 --- a/src/Serializer/Block/FilteredBlockSerializer.php +++ /dev/null @@ -1,67 +0,0 @@ -headerSerializer = $header; - $this->treeSerializer = $tree; - } - - /** - * @param Parser $parser - * @return FilteredBlock - */ - public function fromParser(Parser $parser): FilteredBlock - { - return new FilteredBlock( - $this->headerSerializer->fromParser($parser), - $this->treeSerializer->fromParser($parser) - ); - } - - /** - * @param BufferInterface $data - * @return FilteredBlock - */ - public function parse(BufferInterface $data): FilteredBlock - { - return $this->fromParser(new Parser($data)); - } - - /** - * @param FilteredBlock $merkleBlock - * @return BufferInterface - */ - public function serialize(FilteredBlock $merkleBlock): BufferInterface - { - return Buffertools::concat( - $this->headerSerializer->serialize($merkleBlock->getHeader()), - $this->treeSerializer->serialize($merkleBlock->getPartialTree()) - ); - } -} diff --git a/src/Serializer/Block/PartialMerkleTreeSerializer.php b/src/Serializer/Block/PartialMerkleTreeSerializer.php deleted file mode 100644 index 42ad593e8..000000000 --- a/src/Serializer/Block/PartialMerkleTreeSerializer.php +++ /dev/null @@ -1,122 +0,0 @@ -template = $this->getTemplate(); - } - - /** - * @return Template - */ - public function getTemplate(): Template - { - return (new TemplateFactory()) - ->uint32le() - ->vector(function (Parser $parser) { - return $parser->readBytes(32); - }) - ->vector(function (Parser $parser) { - return $parser->readBytes(1); - }) - ->getTemplate(); - } - - /** - * @param int $last - * @param BufferInterface[] $vBytes - * @return array - */ - private function buffersToBitArray($last, array $vBytes): array - { - $size = count($vBytes) * 8; - $vBits = []; - - for ($p = 0; $p < $size; $p++) { - $byteIndex = (int)floor($p / 8); - $byte = ord($vBytes[$byteIndex]->getBinary()); - $vBits[$p] = (int) (($byte & (1 << ($p % 8))) !== 0); - } - - return array_slice($vBits, 0, $last); - } - - /** - * @param Parser $parser - * @return PartialMerkleTree - */ - public function fromParser(Parser $parser): PartialMerkleTree - { - list ($txCount, $vHash, $vBits) = $this->template->parse($parser); - - return new PartialMerkleTree( - (int)$txCount, - $vHash, - $this->buffersToBitArray($txCount, $vBits) - ); - } - - /** - * @param BufferInterface $buffer - * @return PartialMerkleTree - */ - public function parse(BufferInterface $buffer): PartialMerkleTree - { - return $this->fromParser(new Parser($buffer)); - } - - /** - * @param array $bits - * @return array - */ - private function bitsToBuffers(array $bits): array - { - $vBuffers = str_split(str_pad('', (int)((count($bits)+7)/8), '0', STR_PAD_LEFT)); - $nBits = count($bits); - - for ($p = 0; $p < $nBits; $p++) { - $index = (int)floor($p / 8); - $vBuffers[$index] |= $bits[$p] << ($p % 8); - } - - foreach ($vBuffers as &$value) { - $value = Buffer::int($value); - } - unset($value); - - return $vBuffers; - } - - /** - * @param PartialMerkleTree $tree - * @return BufferInterface - */ - public function serialize(PartialMerkleTree $tree): BufferInterface - { - return $this->template->write([ - $tree->getTxCount(), - $tree->getHashes(), - $this->bitsToBuffers($tree->getFlagBits()) - ]); - } -} diff --git a/src/Serializer/Bloom/BloomFilterSerializer.php b/src/Serializer/Bloom/BloomFilterSerializer.php deleted file mode 100644 index c94532470..000000000 --- a/src/Serializer/Bloom/BloomFilterSerializer.php +++ /dev/null @@ -1,89 +0,0 @@ -uint32le = Types::uint32le(); - $this->uint8le = Types::uint8le(); - $this->varint = Types::varint(); - } - - /** - * @param BloomFilter $filter - * @return BufferInterface - */ - public function serialize(BloomFilter $filter): BufferInterface - { - $parser = new Parser(); - $parser->appendBinary($this->varint->write(count($filter->getData()))); - foreach ($filter->getData() as $i) { - $parser->appendBinary(pack('c', $i)); - } - - $parser->appendBinary($this->uint32le->write($filter->getNumHashFuncs())); - $parser->appendBinary($this->uint32le->write($filter->getTweak())); - $parser->appendBinary($this->uint8le->write($filter->getFlags())); - - return $parser->getBuffer(); - } - - /** - * @param Parser $parser - * @return BloomFilter - */ - public function fromParser(Parser $parser): BloomFilter - { - $varint = (int) $this->varint->read($parser); - $vData = []; - for ($i = 0; $i < $varint; $i++) { - $vData[] = (int) $this->uint8le->read($parser); - } - - $nHashFuncs = (int) $this->uint32le->read($parser); - $nTweak = (int) $this->uint32le->read($parser); - $flags = (int) $this->uint8le->read($parser); - - return new BloomFilter( - Bitcoin::getMath(), - $vData, - $nHashFuncs, - $nTweak, - $flags - ); - } - - /** - * @param BufferInterface $data - * @return BloomFilter - */ - public function parse(BufferInterface $data): BloomFilter - { - return $this->fromParser(new Parser($data)); - } -} diff --git a/tests/Block/FilteredBlockTest.php b/tests/Block/FilteredBlockTest.php deleted file mode 100644 index 66081e993..000000000 --- a/tests/Block/FilteredBlockTest.php +++ /dev/null @@ -1,192 +0,0 @@ -insertData(Buffer::hex('63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5')); - - // Check that FilteredBlock message is serialized correctly - // since it contains a BlockHeader and a PartialMerkleTree - $filtered = $block->filter($filter); - - $serialized = $filtered->getBuffer(); - $this->assertEquals($expectedMerkleBlockPayload, $serialized->getHex()); - - // Check that the serialized NetworkMessage can be parsed again - $serializer = new FilteredBlockSerializer(new BlockHeaderSerializer(), new PartialMerkleTreeSerializer()); - $parsed = $serializer->parse($serialized); - $this->assertEquals($filtered, $parsed); - - $tree = $filtered->getPartialTree(); - /** @var \BitWasp\Buffertools\Buffer[] $matches */ - $matches = []; - $extracted = $tree->extractMatches($matches); - $this->assertEquals($block->getHeader()->getMerkleRoot(), $extracted); - } - - - public function testMerkleBlock() - { - $hex = '0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000'; - $expectedMerkle = '0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930900000002bc56aae9c0b9a19d49250c9bf9bf90b3c1ee3ac9096410a1eb179e1e92f90a66201f4587ec86b58297edc2dd32d6fcd998aa794308aac802a8af3be0e081d674013d'; - $block = BlockFactory::fromHex($hex); - $blockMerkleRoot = $block->getHeader()->getMerkleRoot(); - - $math = new Math(); - $flags = BloomFilter::UPDATE_ALL; - - $tx9 = Buffer::hex('74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20', 32); - $tx8 = Buffer::hex('dd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053'); - - $filter = BloomFilter::create($math, 10, 0.000001, 0, $flags)->insertData($tx9); - - $filtered = $block->filter($filter); - /** @var Buffer[] $matched */ - $matched = []; - $root = $filtered->getPartialTree()->extractMatches($matched); - - $this->assertEquals($blockMerkleRoot, $root); - $this->assertEquals(1, count($matched)); - $this->assertEquals($tx9, $matched[0]); - - // Check serialized FilteredBlock matches proof provided by bitcoind - $this->assertEquals($expectedMerkle, $filtered->getHex()); - - $filter->insertData($tx8); - $filtered = $block->filter($filter); - /** @var Buffer[] $matched */ - $matched = []; - $root = $filtered->getPartialTree()->extractMatches($matched); - $this->assertEquals($blockMerkleRoot, $root); - $this->assertEquals(2, count($matched)); - $this->assertEquals($tx8, $matched[0]); - $this->assertEquals($tx9, $matched[1]); - } - - public function testMerkleBlock2() - { - $hex = '0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f0ae1d4d26e49ffff001d00f0a4410401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d029105ffffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a6294078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc5719aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028010000004a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391fa3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376aaa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a493046022100b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb9005c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000'; - $block = BlockFactory::fromHex($hex); - $blockMerkleRoot = $block->getHeader()->getMerkleRoot(); - - $expectedTx = array_map(function ($value) { - return Buffer::hex($value, 32); - }, [ - 'e980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70', - '28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f', - '6b0f8a73a56c04b519f1883e8aafda643ba61a30bd1439969df21bea5f4e27e2', - '3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23' - ]); - - $math = new Math(); - $flags = BloomFilter::UPDATE_ALL; - - $filter = BloomFilter::create($math, 10, 0.000001, 0, $flags)->insertData($expectedTx[0]); - $filtered = $block->filter($filter); - /** @var Buffer[] $matched */ - $matched = []; - $root = $filtered->getPartialTree()->extractMatches($matched); - - $this->assertEquals($blockMerkleRoot, $root); - $this->assertEquals(1, count($matched)); - $this->assertEquals($expectedTx[0], $matched[0]); - - $filter->insertData(Buffer::hex('044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45af')); - $filtered = $block->filter($filter); - /** @var Buffer[] $matched */ - $matched = []; - $root = $filtered->getPartialTree()->extractMatches($matched); - - $this->assertEquals($blockMerkleRoot, $root); - $this->assertEquals(4, count($matched)); - $cETx = count($expectedTx); - for ($i = 0; $i < $cETx; $i++) { - $this->assertEquals($expectedTx[$i], $matched[$i]); - } - } - - public function testMerkleBlockWithUpdateNone() - { - $hex = '0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f0ae1d4d26e49ffff001d00f0a4410401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d029105ffffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a6294078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc5719aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028010000004a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391fa3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376aaa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a493046022100b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb9005c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000'; - $block = BlockFactory::fromHex($hex); - $blockMerkleRoot = $block->getHeader()->getMerkleRoot(); - - $expectedTx = array_map(function ($value) { - return Buffer::hex($value, 32); - }, [ - 'e980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70', - '28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f', - '3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23' - ]); - - $math = new Math(); - $flags = BloomFilter::UPDATE_NONE; - - $filter = BloomFilter::create($math, 10, 0.000001, 0, $flags)->insertData($expectedTx[0]); - $filtered = $block->filter($filter); - /** @var Buffer[] $matched */ - $matched = []; - $root = $filtered->getPartialTree()->extractMatches($matched); - - $this->assertEquals($blockMerkleRoot, $root); - $this->assertEquals(1, count($matched)); - $this->assertEquals($expectedTx[0], $matched[0]); - - $filter->insertData(Buffer::hex('044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45af')); - $filtered = $block->filter($filter); - $matched = []; - $root = $filtered->getPartialTree()->extractMatches($matched); - - $this->assertEquals($blockMerkleRoot, $root); - $this->assertEquals(3, count($matched)); - $cETx = count($expectedTx); - for ($i = 0; $i < $cETx; $i++) { - $this->assertEquals($expectedTx[$i], $matched[$i]); - } - } - - public function testMerkleBlock3WithSerialize() - { - $hex = '0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff08044c86041b020a02ffffffff0100f2052a01000000434104ecd3229b0571c3be876feaac0442a9f13c5a572742927af1dc623353ecf8c202225f64868137a18cdd85cbbb4c74fbccfd4f49639cf1bdc94a5672bb15ad5d4cac00000000'; - $expectedMerkle = '0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630100000001b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f19630101'; - $block = BlockFactory::fromHex($hex); - $blockMerkleRoot = $block->getHeader()->getMerkleRoot(); - - $math = new Math(); - $flags = BloomFilter::UPDATE_NONE; - - $tx = Buffer::hex('63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5'); - $filter = BloomFilter::create($math, 10, 0.000001, 0, $flags)->insertData($tx); - $filtered = $block->filter($filter); - /** @var Buffer[] $matched */ - $matched = []; - $root = $filtered->getPartialTree()->extractMatches($matched); - - $this->assertEquals($blockMerkleRoot, $root); - $this->assertEquals(1, count($matched)); - $this->assertEquals($tx, $matched[0]); - - $serialized = $filtered->getBuffer()->getHex(); - $this->assertEquals($expectedMerkle, $serialized); - } -} diff --git a/tests/Bloom/BloomFilterTest.php b/tests/Bloom/BloomFilterTest.php deleted file mode 100644 index 001f27ccc..000000000 --- a/tests/Bloom/BloomFilterTest.php +++ /dev/null @@ -1,357 +0,0 @@ -pubKeyFactory = new PublicKeyFactory(); - parent::setUp(); - } - - /** - * @param BufferInterface $hex - * @return BloomFilter - */ - private function parseFilter(BufferInterface $hex) - { - return (new BloomFilterSerializer)->parse($hex); - } - - /** - * @return BloomFilter - * @throws \Exception - */ - private function getEmptyFilterVector() - { - return $this->parseFilter(Buffer::hex('2200000000000000000000000000000000000000000000000000000000000000000000120000000000000001')); - } - - /** - * @return BloomFilter - * @throws \Exception - */ - private function getFullFilterVector() - { - return $this->parseFilter(Buffer::hex('22FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF120000000000000001')); - } - - /** - * @param PublicKeyInterface $publicKey - * @return \BitWasp\Bitcoin\Transaction\TransactionInterface - */ - private function getPayToPubkeyTxVector(PublicKeyInterface $publicKey) - { - return TransactionFactory::build() - ->input('0000000000000000000000000000000000000000000000000000000000000000', 0) - ->output(50 * Amount::COIN, ScriptFactory::scriptPubKey()->payToPubKey($publicKey)) - ->get(); - } - - /** - * @param PublicKeyInterface $publicKey - * @return \BitWasp\Bitcoin\Transaction\TransactionInterface - */ - private function getPayToMultisigTxVector(PublicKeyInterface $publicKey) - { - return TransactionFactory::build() - ->input('0000000000000000000000000000000000000000000000000000000000000000', 0) - ->output(50 * Amount::COIN, ScriptFactory::scriptPubKey()->multisig(1, [$publicKey])) - ->get(); - } - - public function testBasics() - { - $math = new Math(); - $flags = BloomFilter::UPDATE_ALL; - $filter = BloomFilter::create($math, 3, 0.01, 0, $flags); - - $buff = [ - Buffer::hex('99108ad8ed9bb6274d3980bab5a85c048f0950c8'), - Buffer::hex('b5a2c786d9ef4658287ced5914b37a1b4aa32eee'), - Buffer::hex('b9300670b4c5366e95b2699e8b18bc75e5f729c5') - ]; - - $bytes = Buffer::hex('a9030f7dbeb53a6ec0c2a1908b18b4c5eb67c2c1'); - - foreach ($buff as $buf) { - $filter->insertData($buf); - $this->assertTrue($filter->containsData($buf)); - $this->assertFalse($filter->containsData($bytes)); - } - - $this->assertEquals('03614e9b050000000000000001', $filter->getBuffer()->getHex()); - } - - public function testEmptyContains() - { - $math = new Math(); - $flags = BloomFilter::UPDATE_ALL; - $filter = BloomFilter::create($math, 3, 0.01, 0, $flags); - $this->assertFalse($filter->containsData(new Buffer())); - } - - public function testEmptyAcceptableSize() - { - $math = new Math(); - $flags = BloomFilter::UPDATE_ALL; - $filter = BloomFilter::create($math, 3, 0.01, 0, $flags); - $this->assertTrue($filter->hasAcceptableSize()); - } - - public function testEmptyRelevantAndUpdateTx() - { - $math = new Math(); - $flags = BloomFilter::UPDATE_ALL; - $filter = BloomFilter::create($math, 3, 0.01, 0, $flags); - $this->assertFalse($filter->isRelevantAndUpdate(new Transaction())); - } - - public function testBasics2() - { - $math = new Math(); - $flags = BloomFilter::UPDATE_ALL; - $filter = BloomFilter::create($math, 3, 0.01, 2147483649, $flags); - - $buff = [ - Buffer::hex('99108ad8ed9bb6274d3980bab5a85c048f0950c8'), - Buffer::hex('b5a2c786d9ef4658287ced5914b37a1b4aa32eee'), - Buffer::hex('b9300670b4c5366e95b2699e8b18bc75e5f729c5') - ]; - - $bytes = Buffer::hex('4141414141414141414141414141414141414141414141414141414141414141'); - - foreach ($buff as $buf) { - $filter->insertData($buf); - $this->assertTrue($filter->containsData($buf)); - $this->assertFalse($filter->containsData($bytes)); - } - - $this->assertEquals('03ce4299050000000100008001', $filter->getBuffer()->getHex()); - $parser = new BloomFilterSerializer(); - $parse = $parser->parse($filter->getBuffer()); - $this->assertEquals($filter, $parse); - } - - public function testFlagChecks() - { - $math = new Math(); - $flagsAll = BloomFilter::UPDATE_ALL; - $filter = BloomFilter::create($math, 3, 0.01, 2147483649, $flagsAll); - $this->assertTrue($filter->isUpdateAll()); - $this->assertFalse($filter->isUpdateNone()); - $this->assertFalse($filter->isUpdatePubKeyOnly()); - - $flagsNone = BloomFilter::UPDATE_NONE; - $filter = BloomFilter::create($math, 3, 0.01, 2147483649, $flagsNone); - $this->assertTrue($filter->isUpdateNone()); - $this->assertFalse($filter->isUpdatePubKeyOnly()); - $this->assertFalse($filter->isUpdateAll()); - - $flagsP2P = BloomFilter::UPDATE_P2PUBKEY_ONLY; - $filter = BloomFilter::create($math, 3, 0.01, 2147483649, $flagsP2P); - $this->assertTrue($filter->isUpdatePubKeyOnly()); - $this->assertFalse($filter->isUpdateNone()); - $this->assertFalse($filter->isUpdateAll()); - } - - public function testForAFalsePositive() - { - /* - * This test serves to ensure the behaviour of bloom filters. - * 3 known values are inserted into the filter. - * 2 values are checked against the filter - however these are obviously false positives. - * 2 values are checked against the filter, which returns a definite no. - */ - - $math = new Math(); - $flags = BloomFilter::UPDATE_ALL; - $filter = BloomFilter::create($math, 3, 0.01, 2147483649, $flags); - - foreach ([ - Buffer::hex('99108ad8ed9bb6274d3980bab5a85c048f0950c8'), - Buffer::hex('b5a2c786d9ef4658287ced5914b37a1b4aa32eee'), - Buffer::hex('b9300670b4c5366e95b2699e8b18bc75e5f729c5') - ] as $buf) { - $filter->insertData($buf); - $this->assertTrue($filter->containsData($buf)); - } - - $falsePositives = [ - Buffer::hex('a408413bbc084c4875f73149052cc343aa00d0c913fe54d7f6d3821d432fceef'), - Buffer::hex('f7ef30d3f2371e402a1533892155112fb14f783ac7d622e5f4648ad5b61161cf') - ]; - - foreach ($falsePositives as $buf) { - $this->assertTrue($filter->containsData($buf)); - } - - $returnsNotFound = [ - Buffer::hex('4a1'), - Buffer::hex('4190'), - ]; - - foreach ($returnsNotFound as $buf) { - $this->assertFalse($filter->containsData($buf)); - } - } - - public function testInsertKey() - { - $pub = $this->pubKeyFactory->fromHex('045b81f0017e2091e2edcd5eecf10d5bdd120a5514cb3ee65b8447ec18bfc4575c6d5bf415e54e03b1067934a0f0ba76b01c6b9ab227142ee1d543764b69d901e0'); - $math = new Math(); - $flags = BloomFilter::UPDATE_ALL; - $filter = BloomFilter::create($math, 2, 0.001, 0, $flags); - - $filter->insertData($pub->getBuffer()); - $hash = $pub->getPubKeyHash(); - $filter->insertData($hash); - - $this->assertEquals('038fc16b080000000000000001', $filter->getBuffer()->getHex()); - } - - public function testEmptyFilterNeverMatches() - { - $pubkey = $this->pubKeyFactory->fromHex('045b81f0017e2091e2edcd5eecf10d5bdd120a5514cb3ee65b8447ec18bfc4575c6d5bf415e54e03b1067934a0f0ba76b01c6b9ab227142ee1d543764b69d901e0'); - $spends = $this->getPayToPubkeyTxVector($pubkey); - - $filter = $this->getEmptyFilterVector(); - $this->assertFalse($filter->isRelevantAndUpdate($spends)); - } - - public function testFullFilterAlwaysRelevant() - { - $pubkey = $this->pubKeyFactory->fromHex('045b81f0017e2091e2edcd5eecf10d5bdd120a5514cb3ee65b8447ec18bfc4575c6d5bf415e54e03b1067934a0f0ba76b01c6b9ab227142ee1d543764b69d901e0'); - $tx = $this->getPayToPubkeyTxVector($pubkey); - $filter = $this->getFullFilterVector(); - $this->assertTrue($filter->isRelevantAndUpdate($tx)); - } - - public function testFullFilterAlwaysContainsData() - { - $filter = $this->getFullFilterVector(); - $this->assertTrue($filter->containsData(new Buffer('totally unrelated'))); - } - - public function testFullFilterNeverChanges() - { - $filter = $this->getFullFilterVector(); - $serialized = $filter->getBinary(); - - $filter->insertData(new Buffer('new data')); - - $serialized2 = $filter->getBinary(); - $this->assertEquals($serialized, $serialized2); - } - - public function testTxMatchesPayToPubkey() - { - $math = $this->safeMath(); - $pubkey = $this->pubKeyFactory->fromHex('045b81f0017e2091e2edcd5eecf10d5bdd120a5514cb3ee65b8447ec18bfc4575c6d5bf415e54e03b1067934a0f0ba76b01c6b9ab227142ee1d543764b69d901e0'); - - $tx = $this->getPayToPubkeyTxVector($pubkey); - - $filter = BloomFilter::create($math, 10, 0.000001, 0, BloomFilter::UPDATE_P2PUBKEY_ONLY); - $filter->insertData($pubkey->getBuffer()); - $this->assertTrue($filter->isRelevantAndUpdate($tx)); - } - - public function testTxMatchesPayToMultisig() - { - $math = $this->safeMath(); - $pubkey = $this->pubKeyFactory->fromHex('045b81f0017e2091e2edcd5eecf10d5bdd120a5514cb3ee65b8447ec18bfc4575c6d5bf415e54e03b1067934a0f0ba76b01c6b9ab227142ee1d543764b69d901e0'); - - $tx = $this->getPayToMultisigTxVector($pubkey); - - $filter = BloomFilter::create($math, 10, 0.000001, 0, BloomFilter::UPDATE_P2PUBKEY_ONLY); - $filter->insertData($pubkey->getBuffer()); - $this->assertTrue($filter->isRelevantAndUpdate($tx)); - } - - public function testTxMatches() - { - $math = new Math(); - $hex = '01000000010b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e88607d722c190000000008b4830450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a0141046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339ffffffff021bff3d11000000001976a91404943fdd508053c75000106d3bc6e2754dbcff1988ac2f15de00000000001976a914a266436d2965547608b9e15d9032a7b9d64fa43188ac00000000'; - $tx = TransactionFactory::fromHex($hex); - $spends = implode( - '', - array_map( - function ($val) { - return str_pad(dechex($val), 2, '0', STR_PAD_LEFT); - }, - [0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00] - ) - ); - $spendTx = TransactionFactory::fromHex($spends); - - $filter = BloomFilter::create($math, 10, 0.000001, 0, BloomFilter::UPDATE_ALL); - $filter->insertData(Buffer::hex('b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b', 32)); - $this->assertTrue($filter->isRelevantAndUpdate($tx)); - - $filter = BloomFilter::create($math, 10, 0.000001, 0, BloomFilter::UPDATE_ALL); - $filter->insertData(Buffer::hex('b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b')); - $this->assertTrue($filter->isRelevantAndUpdate($tx)); - - $filter = BloomFilter::create($math, 10, 0.000001, 0, BloomFilter::UPDATE_ALL); - $filter->insertData(Buffer::hex('30450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a01')); - $this->assertTrue($filter->isRelevantAndUpdate($tx)); - - $filter = BloomFilter::create($math, 10, 0.000001, 0, BloomFilter::UPDATE_ALL); - $filter->insertData(Buffer::hex('046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339')); - $this->assertTrue($filter->isRelevantAndUpdate($tx)); - - $filter = BloomFilter::create($math, 10, 0.000001, 0, BloomFilter::UPDATE_ALL); - $filter->insertData(Buffer::hex('04943fdd508053c75000106d3bc6e2754dbcff19')); - $this->assertTrue($filter->isRelevantAndUpdate($tx)); - $this->assertTrue($filter->isRelevantAndUpdate($spendTx)); - - $filter = BloomFilter::create($math, 10, 0.000001, 0, BloomFilter::UPDATE_ALL); - $filter->insertData(Buffer::hex('a266436d2965547608b9e15d9032a7b9d64fa431')); - $this->assertTrue($filter->isRelevantAndUpdate($tx)); - - $filter = BloomFilter::create($math, 10, 0.000001, 0, BloomFilter::UPDATE_ALL); - $filter->insertOutPoint(new OutPoint(Buffer::hex('90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b'), 0)); - $this->assertTrue($filter->isRelevantAndUpdate($tx)); - - $filter = BloomFilter::create($math, 10, 0.000001, 0, BloomFilter::UPDATE_ALL); - $filter->insertData(Buffer::hex('0000006d2965547608b9e15d9032a7b9d64fa431')); - $this->assertFalse($filter->isRelevantAndUpdate($tx)); - - $filter = BloomFilter::create($math, 10, 0.000001, 0, BloomFilter::UPDATE_ALL); - $filter->insertOutPoint(new OutPoint(Buffer::hex('41c1d247b5f6ef9952cd711beba91ba216982fb6ba58f3bdaab65e7341414141'), 0)); - $this->assertFalse($filter->isRelevantAndUpdate($tx)); - } - - public function testIsEmpty() - { - $emptyFilter = $this->getEmptyFilterVector(); - $this->assertFalse($emptyFilter->isFull()); - $this->assertTrue($emptyFilter->isEmpty()); - - $fullFilter = $this->getFullFilterVector(); - $this->assertTrue($fullFilter->isFull()); - $this->assertFalse($fullFilter->isEmpty()); - } -} diff --git a/tests/Serializer/Block/PartialMerkleTreeSerializerTest.php b/tests/Serializer/Block/PartialMerkleTreeSerializerTest.php deleted file mode 100644 index 3adc93895..000000000 --- a/tests/Serializer/Block/PartialMerkleTreeSerializerTest.php +++ /dev/null @@ -1,53 +0,0 @@ -insertData($tx9); - - $filtered = $block->filter($filter); - - $tree = $filtered->getPartialTree(); - - $serialized = $tree->getBuffer(); - $serializer = new PartialMerkleTreeSerializer(); - $parsed = $serializer->parse($serialized); - - $treeBits = $tree->getFlagBits(); - $parsedBits = $tree->getFlagBits(); - $last = min(count($treeBits), count($parsedBits)); - for ($i = 0; $i < $last; $i++) { - $this->assertEquals($treeBits[$i], $parsedBits[$i]); - } - - $this->assertEquals($tree->getHashes(), $parsed->getHashes()); - $this->assertEquals($tree->getBuffer(), $parsed->getBuffer()); - - $treeMatch = []; - $tree->extractMatches($treeMatch); - - $parsedMatch = []; - $parsed->extractMatches($parsedMatch); - - $this->assertEquals($treeMatch, $parsedMatch); - } -}