/** * @param BlockHeaderInterface $header * @return $this * @throws \Exception */ public function save(BlockHeaderInterface $header) { $height = $this->height(); $this->index->save('height', ++$height); $key = $this->cacheIndex($height); $this->index->save($key, $header->getBlockHash()); return $this; }
/** * @param BufferInterface $hash * @param BlockHeaderInterface $header * @param bool $checkPow * @return $this */ public function check(BufferInterface $hash, BlockHeaderInterface $header, $checkPow = true) { try { if ($checkPow) { $this->pow->check($hash, $header->getBits()); } } catch (\Exception $e) { throw new \RuntimeException('Headers::check() - failed validating header proof-of-work'); } return $this; }
/** * @param ChainState $state * @param BlockHeaderInterface $header * @return BlockIndex */ public function accept(ChainState $state, BlockHeaderInterface $header) { $hash = $header->getHash(); if ($state->getChain()->containsHash($hash)) { // todo: check for rejected block return $this->db->fetchIndex($hash); } $prevIndex = $state->getChain()->getIndex(); $index = $this->headerCheck->check($header)->checkContextual($state, $header)->makeIndex($prevIndex, $header); $this->db->insertIndexBatch($prevIndex, [$index]); $state->getChain()->updateTip($index); return $index; }
/** * @param BlockHeaderInterface $header * @return bool * @throws \Exception */ public function checkHeader(BlockHeaderInterface $header) { $math = $this->math; $target = $this->difficulty->getTarget($header->getBits()); if ($math->cmp($target, 0) == 0 || $math->cmp($target, $this->limit()) > 0) { throw new \Exception('nBits below minimum work'); } $hashInt = $math->hexDec($header->getBlockHash()); if ($math->cmp($hashInt, $target) > 0) { throw new \Exception("Hash doesn't match nBits"); } return true; }
/** * @param BlockHeaderInterface $header * @return Chain */ public function findTipForNext(BlockHeaderInterface $header) { foreach ($this->states as $state) { $tTip = $state->getChain(); $tipHash = $tTip->getIndex()->getHash(); if ($header->getPrevBlock() == $tipHash) { $tip = $tTip; } } if (!isset($tip)) { throw new \RuntimeException('No tip found for this Header'); } return $tip; }
/** * @param BlockHeaderInterface $header * @return $this */ public function delete(BlockHeaderInterface $header) { $hash = $header->getBlockHash(); $this->hash()->delete($this->height()->fetch($hash)); $this->height()->delete($hash); return $this; }
/** * Process a block against the given state of the chain. * @param BlockHeaderInterface $header * @return bool */ public function process(BlockHeaderInterface $header) { // Ignore the genesis block $hash = $header->getBlockHash(); if ($hash === $this->genesis->getBlockHash()) { return true; } if ($this->index()->height()->contains($hash)) { return true; } try { // Attempt to add it to the chain $this->add($header); $this->pow->checkHeader($header); $result = true; } catch (BlockPrevNotFound $e) { // If it fails because it doesn't elongate the chain, process it as an orphan. // Result will be determined $result = $this->processOrphan($header); } catch (BlockPowError $e) { $result = false; // Invalid block. } return $result; }
/** * @param TransactionInterface|null $coinbaseTx * @return Block * @throws \BitWasp\Bitcoin\Exceptions\MerkleTreeEmpty */ public function run(TransactionInterface $coinbaseTx = null) { $nonce = '0'; $maxNonce = $this->math->pow(2, 32); // Allow user supplied transactions if ($coinbaseTx == null) { $coinbaseTx = new Transaction(); $coinbaseTx->getInputs()->addInput(new TransactionInput('0000000000000000000000000000000000000000000000000000000000000000', 4294967295.0)); $coinbaseTx->getOutputs()->addOutput(new TransactionOutput(5000000000.0, $this->script)); } $inputs = $coinbaseTx->getInputs(); $found = false; $usingDiff = $this->lastBlockHeader->getBits(); $diff = new Difficulty($this->math); $target = $diff->getTarget($usingDiff); while (false === $found) { // Set coinbase script, and build Merkle tree & block header. $inputs->getInput(0)->setScript($this->getCoinbaseScriptBuf()); $transactions = new TransactionCollection(array_merge(array($coinbaseTx), $this->transactions->getTransactions())); $merkleRoot = new MerkleRoot($this->math, $transactions); $merkleHash = $merkleRoot->calculateHash(); $header = new BlockHeader($this->version, $this->lastBlockHeader->getBlockHash(), $merkleHash, $this->timestamp, $usingDiff, '0'); $t = microtime(true); // Loop through all nonces (up to 2^32). Restart after modifying extranonce. while ($this->math->cmp($header->getNonce(), $maxNonce) <= 0) { $header->setNonce($this->math->add($header->getNonce(), '1')); $hash = (new Parser())->writeBytes(32, Hash::sha256d($header->getBuffer()), true)->getBuffer(); if ($this->math->cmp($hash->getInt(), $target) <= 0) { $block = new Block($this->math, $header, $transactions); return $block; } if ($this->report && $this->math->cmp($this->math->mod($header->getNonce(), 100000), '0') == 0) { $time = microtime(true) - $t; $khash = $nonce / $time / 1000; echo "extraNonce[{$this->extraNonce}] nonce[{$nonce}] time[{$time}] khash/s[{$khash}] \n"; } } // Whenever we exceed 2^32, increment extraNonce and reset $nonce $this->extraNonce++; $nonce = '0'; } }
/** * @param BlockHeaderInterface $header * @return bool * @throws \Exception */ public function checkHeader(BlockHeaderInterface $header) { return $this->check($header->getHash(), $header->getBits()->getInt()); }
/** * @param BlockIndex $prevIndex * @param BlockHeaderInterface $header * @return BlockIndex */ public function makeIndex(BlockIndex $prevIndex, BlockHeaderInterface $header) { return new BlockIndex($header->getHash()->getHex(), $this->math->add($prevIndex->getHeight(), 1), $this->math->add($this->pow->getWork($header->getBits()), $prevIndex->getWork()), $header); }
/** * @param BlockHeaderInterface $header * @return BlockIndexInterface|bool */ public function hasBlockTip(BlockHeaderInterface $header) { echo "CheckHashBlockTip - " . count($this->segments) . PHP_EOL; foreach ($this->segments as $segment) { /** @var BlockIndexInterface $segBlock */ $segBlock = $this->segmentBlock->offsetGet($segment); if ($header->getPrevBlock()->equals($segBlock->getHash())) { return $segBlock; } } return false; }
/** * @param BufferInterface $hash * @param BlockIndexInterface $prevIndex * @param BlockHeaderInterface $header * @return BlockIndex */ public function getNextIndex(BufferInterface $hash, BlockIndexInterface $prevIndex, BlockHeaderInterface $header) { return new BlockIndex($hash, $prevIndex->getHeight() + 1, $this->math->toString($this->math->add($this->proofOfWork->getWork($header->getBits()), gmp_init($prevIndex->getWork()))), $header); }
/** * @param BlockHeaderInterface $header * @return \BitWasp\Buffertools\Buffer */ public function serialize(BlockHeaderInterface $header) { return $this->getTemplate()->write([$header->getVersion(), Buffer::hex($header->getPrevBlock()), Buffer::hex($header->getMerkleRoot()), $header->getTimestamp(), $header->getBits(), $header->getNonce()]); }
/** * @param Index\Blocks $blocks * @param int $currentHeight * @param BlockHeaderInterface $header * @return bool */ public function scriptVerifyDerSig(Index\Blocks $blocks, $currentHeight, BlockHeaderInterface $header) { if ($this->math->cmp($header->getVersion(), 3) && $this->isSuperMajority(3, $currentHeight - 1, $blocks, $this->params->majorityEnforceBlockUpgrade())) { return true; } return false; }
/** * Creates the Genesis block index * @param BlockHeaderInterface $header * @return bool */ public function createIndexGenesis(BlockHeaderInterface $header) { $stmtHeader = $this->dbh->prepare('INSERT INTO headerIndex ( hash, segment, height, work, version, prevBlock, merkleRoot, nBits, nTimestamp, nNonce ) VALUES ( :hash, :segment, :height, :work, :version, :prevBlock, :merkleRoot, :nBits, :nTimestamp, :nNonce ) '); if ($stmtHeader->execute(array('hash' => $header->getHash()->getBinary(), 'segment' => 0, 'height' => 0, 'work' => 0, 'version' => $header->getVersion(), 'prevBlock' => $header->getPrevBlock()->getBinary(), 'merkleRoot' => $header->getMerkleRoot()->getBinary(), 'nBits' => $header->getBits(), 'nTimestamp' => $header->getTimestamp(), 'nNonce' => $header->getNonce()))) { return true; } throw new \RuntimeException('Failed to update insert Genesis block index!'); }
/** * @param BlockHeaderInterface $header * @return string */ private function cacheIndexHeader(BlockHeaderInterface $header) { return "blkheight_" . $header->getBlockHash(); }
/** * @param $timeFirstBlock * @param BlockHeaderInterface $header * @return mixed */ public function calculateWorkTimespan($timeFirstBlock, BlockHeaderInterface $header) { $timespan = $header->getTimestamp() - $timeFirstBlock; $lowest = $this->params->powTargetTimespan() / 4; $highest = $this->params->powTargetTimespan() * 4; if ($timespan < $lowest) { $timespan = $lowest; } if ($timespan > $highest) { $timespan = $highest; } return $timespan; }
/** * @param BlockHeaderInterface $blk * @return string */ private function cacheIndexBlk(BlockHeaderInterface $blk) { return $this->cacheIndex($blk->getBlockHash()); }