/** * @param BlockInterface $block * @return $this */ public function check(BlockInterface $block) { $header = $block->getHeader(); if ($block->getMerkleRoot() !== $header->getMerkleRoot()) { throw new \RuntimeException('Blocks::check(): failed to verify merkle root'); } $transactions = $block->getTransactions(); $txCount = count($transactions); if ($txCount == 0 || $block->getBuffer()->getSize() > $this->params->maxBlockSizeBytes()) { throw new \RuntimeException('Blocks::check(): Zero transactions, or block exceeds max size'); } // The first transaction is coinbase, and only the first transaction is coinbase. if (!$transactions[0]->isCoinbase()) { throw new \RuntimeException('Blocks::check(): First transaction was not coinbase'); } for ($i = 1; $i < $txCount; $i++) { if ($transactions->offsetGet($i)->isCoinbase()) { throw new \RuntimeException('Blocks::check(): more than one coinbase'); } } $nSigOps = 0; foreach ($transactions as $transaction) { if (!$this->checkTransaction($transaction)) { throw new \RuntimeException('Blocks::check(): failed checkTransaction'); } $nSigOps += $this->getLegacySigOps($transaction); } if ($this->math->cmp($nSigOps, $this->params->getMaxBlockSigOps()) > 0) { throw new \RuntimeException('Blocks::check(): out-of-bounds sigop count'); } return $this; }
/** * */ private function update() { $header = $this->index->getHeader(); // Check all active features if ($header->getTimestamp() >= $this->params->p2shActivateTime()) { $this->p2sh = true; } $hash = $this->index->getHash()->getBinary(); $this->bip30 = !($this->index->getHeight() == 91842 && $hash == pack("H*", "00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec") || $this->index->getHeight() == 91880 && $hash == pack("H*", "00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")); $highest = $this->majorityVersion(); if ($highest >= 2) { $this->bip34 = true; } if ($highest >= 3) { $this->derSig = true; } if ($highest >= 4) { $this->cltv = true; } // Calculate flags $this->flags = $this->p2sh ? InterpreterInterface::VERIFY_NONE : InterpreterInterface::VERIFY_P2SH; if ($this->derSig) { $this->flags |= InterpreterInterface::VERIFY_DERSIG; } if ($this->cltv) { $this->flags |= InterpreterInterface::VERIFY_CHECKLOCKTIMEVERIFY; } }
/** * @param ChainSegment $segment * @param BlockIndexInterface $index */ public function updateSegment(ChainSegment $segment, BlockIndexInterface $index) { $prevBits = $segment->getLast()->getHeader()->getBits(); $segment->next($index); $this->hashStorage[$segment->getId()][$index->getHash()->getBinary()] = $index->getHeight(); $this->heightStorage[$segment->getId()][$index->getHeight()] = $index->getHash()->getBinary(); if ($index->getHeight() % $this->params->powRetargetInterval() === 0) { $this->emit('retarget', [$segment, $prevBits, $index]); } $this->updateGreatestWork(); }
/** * @param int $minVersion * @param int $startHeight * @param int $nRequired * @param Index\Blocks $blocks * @return bool */ public function isSuperMajority($minVersion, $startHeight, Index\Blocks $blocks, $nRequired) { $nFound = 0; $window = $this->params->majorityWindow(); for ($i = 0; $i < $window && $nFound < $nRequired && ($index = $blocks->fetchByHeight($startHeight - $i)); $i++) { if ($this->math->cmp($index->getHeader()->getVersion(), $minVersion)) { $nFound++; } } return $nFound >= $nRequired; }
/** * BetterNode constructor. * @param ConfigProviderInterface $config * @param ParamsInterface $params * @param DbInterface $db */ public function __construct(ConfigProviderInterface $config, ParamsInterface $params, DbInterface $db) { $math = Bitcoin::getMath(); $adapter = Bitcoin::getEcAdapter($math); $this->chains = new ChainContainer($math, $params); $consensus = new Consensus($math, $params); $pow = new ProofOfWork($math, $params); $this->headers = new Index\Headers($db, $adapter, $this->chains, $consensus, $pow); $this->blocks = new Index\Blocks($db, $config, $adapter, $this->chains, $consensus); $this->transactions = new Index\Transactions($db); $genesis = $params->getGenesisBlock(); $this->headers->init($genesis->getHeader()); $this->blocks->init($genesis); $this->db = $db; $segments = $this->db->fetchChainSegments(); foreach ($segments as $segment) { $this->chains->addSegment($segment); } $this->chains->initialize($this->db); }
/** * @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; }
/** * @return int|string */ public function getMaxTarget() { return $this->getTarget(Buffer::int($this->params->powBitsLimit(), 4, $this->math)); }
/** * @param ParamsInterface $params * @param LoopInterface $loop */ public function __construct(ParamsInterface $params, LoopInterface $loop) { echo ' [App] start ' . PHP_EOL; $start = microtime(true); $math = Bitcoin::getMath(); $adapter = Bitcoin::getEcAdapter($math); $zmq = new ZMQContext($loop); $this->initControl($zmq)->initConfig(); $this->loop = $loop; $this->params = $params; $this->adapter = $adapter; $this->chains = new Chains($adapter); $this->inventory = new KnownInventory(); $this->peerState = new PeerStateCollection(); $this->peersInbound = new Peers(); $this->peersOutbound = new Peers(); $this->netFactory = new NetworkingFactory($loop); $this->db = new Db($this->config, false); $consensus = new Consensus($math, $params); $zmqScript = new ZmqScriptCheck(new \ZMQContext()); $this->headers = new Index\Headers($this->db, $consensus, $math, new HeaderCheck($consensus, $adapter, new ProofOfWork($math, $params))); $this->blocks = new Index\Blocks($this->db, $adapter, $consensus, new BlockCheck($consensus, $adapter, $zmqScript)); $genesis = $params->getGenesisBlock(); $this->headers->init($genesis->getHeader()); $this->blocks->init($genesis); $this->initChainState(); $this->utxo = new Index\UtxoIdx($this->chains, $this->db); $this->blockDownload = new BlockDownloader($this->chains, $this->peerState, $this->peersOutbound); $this->on('blocks.syncing', function () { echo ' [App] ... BLOCKS: syncing' . PHP_EOL; }); $this->on('headers.syncing', function () { echo ' [App] ... HEADERS: syncing' . PHP_EOL; }); $this->on('headers.synced', function () { echo ' [App] ... HEADERS: synced!' . PHP_EOL; }); echo ' [App] Startup took: ' . (microtime(true) - $start) . ' seconds ' . PHP_EOL; }