/** * @param ChainState $state * @param ChainCache $chainView * @param Peer $peer * @param Inventory[] $items */ public function advertised(ChainState $state, ChainCache $chainView, Peer $peer, array $items) { $chain = $state->getChain(); $fetch = []; $lastUnknown = null; foreach ($items as $inv) { $hash = $inv->getHash(); if ($chain->containsHash($hash)) { if (!$chainView->containsHash($hash)) { $fetch[] = $inv; } } else { $lastUnknown = $hash; } } if (null !== $lastUnknown) { echo "send headers\n"; $peer->getheaders($state->getHeadersLocator($lastUnknown)); $this->peerState->fetch($peer)->updateBlockAvailability($state, $lastUnknown); } if (count($fetch) > 0) { echo 'SEND GETDATA:' . count($fetch) . '\\n'; $peer->getdata($fetch); } }
/** * @param ChainState $state * @param Buffer $hash */ public function updateBlockAvailability(ChainState $state, Buffer $hash) { $chain = $state->getChain(); if ($chain->containsHash($hash)) { echo 'update peers BESTKNOWN block (' . $chain->getHeightFromHash($hash) . ')' . PHP_EOL; $this->save(self::INDEXBESTKNOWNBLOCK, $hash); } else { echo 'update peers HASH UNKNOWN block' . PHP_EOL; $this->save(self::HASHLASTUNKNOWNBLOCK, $hash); } }
/** * @param ChainState $state * @param Buffer $startHash * @throws \RuntimeException * @throws \Exception * @return array */ private function relativeNextInventory(ChainState $state, Buffer $startHash) { $best = $state->getChain(); if (!$best->containsHash($startHash)) { throw new \RuntimeException('Hash not found in this chain'); } $startHeight = $best->getHeightFromHash($startHash) + 1; $stopHeight = min($startHeight + self::DOWNLOAD_AMOUNT, $best->getIndex()->getHeight()); $nInFlight = count($this->inFlight); $request = []; for ($i = $startHeight; $i < $stopHeight && $nInFlight < self::MAX_IN_FLIGHT; $i++) { $request[] = Inventory::block($best->getHashFromHeight($i)); $nInFlight++; } return $request; }
/** * @param ChainState $state * @param BlockInterface $block * @param Headers $headers * @param UtxoIdx $utxoIdx * @return BlockIndex */ public function accept(ChainState $state, BlockInterface $block, Headers $headers, UtxoIdx $utxoIdx) { $bestBlock = $state->getLastBlock(); if ($bestBlock->getHash() !== $block->getHeader()->getPrevBlock()) { throw new \RuntimeException('Blocks:accept() Block does not extend this chain!'); } $index = $headers->accept($state, $block->getHeader()); $this->blockCheck->check($block)->checkContextual($block, $bestBlock); //$view = $utxoIdx->fetchView($state, $block); $view = $this->db->fetchUtxoView($block); $flagP2sh = $this->consensus->scriptVerifyPayToScriptHash($bestBlock->getHeader()->getTimestamp()); $flags = new Flags($flagP2sh ? InterpreterInterface::VERIFY_P2SH : InterpreterInterface::VERIFY_NONE); $nInputs = 0; $nFees = 0; $nSigOps = 0; $txs = $block->getTransactions(); foreach ($block->getTransactions() as $tx) { $nInputs += count($tx->getInputs()); $nSigOps += $this->blockCheck->getLegacySigOps($tx); if ($nSigOps > $this->consensus->getParams()->getMaxBlockSigOps()) { throw new \RuntimeException('Blocks::accept() - too many sigops'); } if (!$tx->isCoinbase()) { if ($flagP2sh) { $nSigOps = $this->blockCheck->getP2shSigOps($view, $tx); if ($nSigOps > $this->consensus->getParams()->getMaxBlockSigOps()) { throw new \RuntimeException('Blocks::accept() - too many sigops'); } } $fee = $this->math->sub($view->getValueIn($this->math, $tx), $tx->getValueOut()); $nFees = $this->math->add($nFees, $fee); $this->blockCheck->checkInputs($view, $tx, $index->getHeight(), $flags); } } $this->blockCheck->checkCoinbaseSubsidy($txs[0], $nFees, $index->getHeight()); $this->db->insertBlock($block); $state->updateLastBlock($index); return $index; }
/** * @param ChainState $state * @return int|string */ public function getWorkRequired(ChainState $state) { $math = $this->math; $index = $state->getChain()->getIndex(); if ($math->cmp($math->mod($math->add($index->getHeight(), 1), $this->params->powRetargetInterval()), 0) != 0) { // No change in difficulty return $index->getHeader()->getBits()->getInt(); } // Retarget $heightLastRetarget = $math->sub($index->getHeight(), $math->sub($this->params->powRetargetInterval(), 1)); $lastTime = $state->getChain()->fetchAncestor($heightLastRetarget)->getHeader()->getTimestamp(); return $this->calculateNextWorkRequired($index, $lastTime); }
/** * @param ChainState $state * @param BlockHeaderInterface[] $headers * @return bool * @throws \Exception */ public function acceptBatch(ChainState $state, array $headers) { $tip = $state->getChain(); $batch = array(); $startIndex = $tip->getIndex(); foreach ($headers as $header) { if ($tip->containsHash($header->getHash())) { continue; } $prevIndex = $tip->getIndex(); if ($prevIndex->getHash() !== $header->getPrevBlock()) { throw new \RuntimeException('Header mismatch, header.prevBlock does not refer to tip'); } $index = $this->headerCheck->check($header)->checkContextual($state, $header)->makeIndex($prevIndex, $header); $tip->updateTip($index); $batch[] = $tip->getIndex(); } // Do a batch update of the chain if (count($batch) > 0) { $this->db->insertIndexBatch($startIndex, $batch); unset($batch); } return true; }
public function compareChainStateWork(ChainState $a, ChainState $b) { return $this->adapter->getMath()->cmp($a->getChain()->getIndex()->getWork(), $b->getChain()->getIndex()->getWork()); }