/** * OutPoint constructor. * @param BufferInterface $hashPrevOutput * @param int $nPrevOutput */ public function __construct(BufferInterface $hashPrevOutput, $nPrevOutput) { if ($hashPrevOutput->getSize() !== 32) { throw new \InvalidArgumentException('OutPoint: hashPrevOut must be a 32-byte Buffer'); } $this->hashPrevOutput = $hashPrevOutput; $this->nPrevOutput = $nPrevOutput; $this->initFunctionAlias('txid', 'getTxId')->initFunctionAlias('vout', 'getVout'); }
/** * @param BlockIndexInterface $index * @return bool */ public function isNext(BlockIndexInterface $index) { if (false === $this->hash->equals($index->getHeader()->getPrevBlock())) { return false; } if (false === ($index->getHeight() == $this->height + 1)) { return false; } return true; }
/** * @param int $type * @param BufferInterface $hash */ public function __construct($type, BufferInterface $hash) { if (false === $this->checkType($type)) { throw new \InvalidArgumentException('Invalid type in InventoryVector'); } if (false === (32 === $hash->getSize())) { throw new \InvalidArgumentException('Hash size must be 32 bytes'); } $this->type = $type; $this->hash = $hash; }
/** * WitnessAddress constructor. * @param int $witnessVersion * @param BufferInterface $hash */ public function __construct($witnessVersion, BufferInterface $hash) { if (!is_int($witnessVersion)) { throw new \RuntimeException('Witness version must be an integer'); } if ($hash->getSize() !== 32) { throw new \RuntimeException('Hash for P2WSH address must be 32 bytes'); } $this->witnessVersion = $witnessVersion; $this->hash = $hash->getHex(); }
/** * @param BufferInterface $hash * @param int|string $nBits * @return bool */ public function check(BufferInterface $hash, $nBits) { $negative = false; $overflow = false; $target = $this->math->writeCompact($nBits, $negative, $overflow); if ($negative || $overflow || $this->math->cmp($target, 0) === 0 || $this->math->cmp($target, $this->getMaxTarget()) > 0) { throw new \RuntimeException('nBits below minimum work'); } if ($this->math->cmp($hash->getInt(), $target) > 0) { throw new \RuntimeException("Hash doesn't match nBits"); } return true; }
/** * Generate a master private key given a * @param BufferInterface $seed * @param EcAdapterInterface $ecAdapter * @return ElectrumKey */ public static function generateMasterKey(BufferInterface $seed, EcAdapterInterface $ecAdapter = null) { // Really weird, did electrum actually hash hex string seeds? $binary = $oldseed = $seed->getHex(); // Perform sha256 hash 5 times per iteration for ($i = 0; $i < 5 * 20000; $i++) { // Hash should return binary data $binary = hash('sha256', $binary . $oldseed, true); } $ecAdapter = $ecAdapter ?: Bitcoin::getEcAdapter(); // Convert binary data to hex. $str = new Buffer($binary, 32, $ecAdapter->getMath()); return self::fromSecretExponent($str->getInt(), $ecAdapter); }
/** * @param BufferInterface $entropy * @return array */ public function entropyToWords(BufferInterface $entropy) { $math = $this->ecAdapter->getMath(); $ENT = $entropy->getSize() * 8; $CS = $ENT / 32; $entBits = $math->baseConvert($entropy->getHex(), 16, 2); $csBits = $this->calculateChecksum($entropy, $CS); $bits = str_pad($entBits . $csBits, $ENT + $CS, '0', STR_PAD_LEFT); $result = []; foreach (str_split($bits, 11) as $bit) { $idx = $math->baseConvert($bit, 2, 10); $result[] = $this->wordList->getWord($idx); } return $result; }
/** * @param int|string $version * @param BufferInterface $prevBlock * @param BufferInterface $merkleRoot * @param int|string $timestamp * @param BufferInterface $bits * @param int|string $nonce */ public function __construct($version, BufferInterface $prevBlock, BufferInterface $merkleRoot, $timestamp, BufferInterface $bits, $nonce) { if ($prevBlock->getSize() !== 32) { throw new \InvalidArgumentException('Block header prevBlock must be a 32-byte Buffer'); } if ($merkleRoot->getSize() !== 32) { throw new \InvalidArgumentException('Block header prevBlock must be a 32-byte Buffer'); } $this->version = $version; $this->prevBlock = $prevBlock; $this->merkleRoot = $merkleRoot; $this->timestamp = $timestamp; $this->bits = $bits; $this->nonce = $nonce; $this->initFunctionAlias('version', 'getVersion')->initFunctionAlias('prevBlock', 'getPrevBlock')->initFunctionAlias('merkleRoot', 'getMerkleRoot')->initFunctionAlias('timestamp', 'getTimestamp')->initFunctionAlias('bits', 'getBits')->initFunctionAlias('nonce', 'getNonce'); }
/** * @param BufferInterface $sig * @return bool * @throws SignatureNotCanonical */ public static function isDERSignature(BufferInterface $sig) { $checkVal = function ($fieldName, $start, $length, $binaryString) { if ($length === 0) { throw new SignatureNotCanonical('Signature ' . $fieldName . ' length is zero'); } $typePrefix = ord(substr($binaryString, $start - 2, 1)); if ($typePrefix !== 0x2) { throw new SignatureNotCanonical('Signature ' . $fieldName . ' value type mismatch'); } $val = substr($binaryString, $start, $length); $vAnd = $val[0] & chr(0x80); if (ord($vAnd) === 128) { throw new SignatureNotCanonical('Signature ' . $fieldName . ' value is negative'); } if ($length > 1 && $val[0] === "" && !ord($val[1] & chr(0x80))) { throw new SignatureNotCanonical('Signature ' . $fieldName . ' value excessively padded'); } }; $bin = $sig->getBinary(); $size = $sig->getSize(); if ($size < 9) { throw new SignatureNotCanonical('Signature too short'); } if ($size > 73) { throw new SignatureNotCanonical('Signature too long'); } if (ord($bin[0]) !== 0x30) { throw new SignatureNotCanonical('Signature has wrong type'); } if (ord($bin[1]) !== $size - 3) { throw new SignatureNotCanonical('Signature has wrong length marker'); } $lenR = ord($bin[3]); $startR = 4; if (5 + $lenR >= $size) { throw new SignatureNotCanonical('Signature S length misplaced'); } $lenS = ord($bin[5 + $lenR]); $startS = 4 + $lenR + 2; if ($lenR + $lenS + 7 !== $size) { throw new SignatureNotCanonical('Signature R+S length mismatch'); } $checkVal('R', $startR, $lenR, $bin); $checkVal('S', $startS, $lenS, $bin); return true; }
/** * @param BufferInterface $entropy * @return array * @throws \Exception */ public function entropyToWords(BufferInterface $entropy) { $math = $this->ecAdapter->getMath(); $n = count($this->wordList); $wordArray = []; $chunks = $entropy->getSize() / 4; for ($i = 0; $i < $chunks; $i++) { $x = $entropy->slice(4 * $i, 4)->getInt(); $index1 = $math->mod($x, $n); $index2 = $math->mod($math->add($math->div($x, $n), $index1), $n); $index3 = $math->mod($math->add($math->div($math->div($x, $n), $n), $index2), $n); $wordArray[] = $this->wordList->getWord($index1); $wordArray[] = $this->wordList->getWord($index2); $wordArray[] = $this->wordList->getWord($index3); } return $wordArray; }
/** * @param BufferInterface $publicKey * @return bool */ public static function isCompressedOrUncompressed(BufferInterface $publicKey) { $vchPubKey = $publicKey->getBinary(); if ($publicKey->getSize() < 33) { return false; } if (ord($vchPubKey[0]) === 0x4) { if ($publicKey->getSize() !== 65) { // Invalid length for uncompressed key return false; } } elseif (in_array($vchPubKey[0], array(hex2bin(self::KEY_COMPRESSED_EVEN), hex2bin(self::KEY_COMPRESSED_ODD)))) { if ($publicKey->getSize() !== 33) { return false; } } else { return false; } return true; }
/** * Encode a given hex string in base58 * * @param BufferInterface $binary * @return string * @throws \Exception */ public static function encode(BufferInterface $binary) { $size = $binary->getSize(); if ($binary->getBinary() === '') { return ''; } $math = Bitcoin::getMath(); $orig = $binary->getBinary(); $decimal = $binary->getInt(); $return = ''; while ($math->cmp($decimal, 0) > 0) { list($decimal, $rem) = $math->divQr($decimal, 58); $return .= self::$base58chars[$rem]; } $return = strrev($return); //leading zeros for ($i = 0; $i < $size && $orig[$i] === ""; $i++) { $return = '1' . $return; } return $return; }
/** * @param BufferInterface $buffer * @return int */ private function parseBuffer(BufferInterface $buffer) { $size = $buffer->getSize(); if ($size === 0) { return '0'; } $chars = array_map(function ($binary) { return ord($binary); }, str_split($buffer->getBinary(), 1)); $result = 0; for ($i = 0; $i < $size; $i++) { $mul = $this->math->mul($i, 8); $byte = $this->math->leftShift($chars[$i], $mul); $result = $this->math->bitwiseOr($result, $byte); } if ($chars[count($chars) - 1] & 0x80) { $mask = gmp_strval(gmp_com($this->math->leftShift(0x80, 8 * ($size - 1))), 10); return $this->math->sub(0, $this->math->bitwiseAnd($result, $mask)); } return $result; }
/** * Push data into the stack. * * @param $data * @return $this * @throws \Exception */ public function push(BufferInterface $data) { $length = $data->getSize(); $parsed = new Parser('', $this->math); /** Note that larger integers are serialized without flipping bits - Big endian */ if ($length < $this->opcodes->getOpByName('OP_PUSHDATA1')) { $varInt = Buffertools::numToVarInt($length); $data = new Buffer($varInt->getBinary() . $data->getBinary(), null, $this->math); $parsed->writeBytes($data->getSize(), $data); } else { if ($length <= 0xff) { $lengthSize = 1; } elseif ($length <= 0xffff) { $lengthSize = 2; } else { $lengthSize = 4; } $op = $this->opcodes->getOpByName('OP_PUSHDATA' . $lengthSize); $parsed->writeBytes(1, Buffer::int($op))->writeBytes($lengthSize, Buffer::int($length), true)->writeBytes($length, $data); } $this->script .= $parsed->getBuffer()->getBinary(); return $this; }
/** * @param BufferInterface $hash * @return $this */ public function markReceived(BufferInterface $hash) { unset($this->inFlight[$hash->getBinary()]); return $this; }
/** * @param BufferInterface $hash * @return bool|ChainView */ public function isTip(BufferInterface $hash) { foreach ($this->segments as $segment) { if ($hash->equals($segment->getLast()->getHash())) { return $this->view($segment); } } return false; }
/** * @param BufferInterface $hash * @param int $numAncestors * @return array */ public function findSuperMajorityInfoByHash(BufferInterface $hash, $numAncestors = 1000) { $this->fetchLftRgtByHash->execute(['hash' => $hash->getBinary()]); $id = $this->fetchLftRgtByHash->fetch(\PDO::FETCH_ASSOC); $this->fetchSuperMajorityVersions->execute(['lft' => $id['lft'], 'rgt' => $id['rgt']]); $stream = $this->fetchSuperMajorityVersions->fetchAll(\PDO::FETCH_COLUMN); return $stream; }
/** * @param PublicKeyInterface $publicKey * @return bool */ public function checkInvolvesKey(PublicKeyInterface $publicKey) { return $publicKey->getPubKeyHash()->getBinary() === $this->hash->getBinary(); }
/** * @param BufferInterface $script * @param Opcodes|null $opCodes */ public function __construct(BufferInterface $script = null, Opcodes $opCodes = null) { $this->script = $script instanceof BufferInterface ? $script->getBinary() : ''; $this->opCodes = $opCodes ?: new Opcodes(); }
/** * @param BufferInterface $publicKey * @return PublicKeyInterface * @throws \Exception */ public function publicKeyFromBuffer(BufferInterface $publicKey) { $compressed = $publicKey->getSize() == PublicKey::LENGTH_COMPRESSED; $xCoord = $publicKey->slice(1, 32)->getInt(); return new PublicKey($this, $this->getGenerator()->getCurve()->getPoint($xCoord, $compressed ? $this->recoverYfromX($xCoord, $publicKey->slice(0, 1)->getHex()) : $publicKey->slice(33, 32)->getInt()), $compressed); }
/** * @param BufferInterface $msg32 * @param PrivateKey $privateKey * @return CompactSignature */ private function doSignCompact(BufferInterface $msg32, PrivateKey $privateKey) { $sig_t = ''; /** @var resource $sig_t */ if (1 !== secp256k1_ecdsa_sign_recoverable($this->context, $msg32->getBinary(), $privateKey->getBinary(), $sig_t)) { throw new \RuntimeException('Secp256k1: failed to sign'); } $recid = ''; $ser = ''; if (!secp256k1_ecdsa_recoverable_signature_serialize_compact($this->context, $sig_t, $ser, $recid)) { throw new \RuntimeException('Failed to obtain recid'); } unset($ser); return new CompactSignature($this, $sig_t, $recid, $privateKey->isCompressed()); }
/** * Do HMAC hashing on $data and $salt * * @param string $algo * @param BufferInterface $data * @param BufferInterface $salt * @return BufferInterface */ public static function hmac($algo, BufferInterface $data, BufferInterface $salt) { return new Buffer(hash_hmac($algo, $data->getBinary(), $salt->getBinary(), true)); }
/** * @param BufferInterface $buffer * @return int */ public function getHeightFromHash(BufferInterface $buffer) { $binary = $buffer->getBinary(); foreach ($this->segments as $segment) { $hashes = $this->container->getHashes($segment); if (isset($hashes[$binary])) { return $hashes[$binary]; } } throw new \RuntimeException('Hash not found'); }
/** * @param BufferInterface $hash */ public function __construct(BufferInterface $hash) { $this->hash = $hash->getHex(); }
/** * @param BufferInterface $signature * @param int $flags * @return $this * @throws \BitWasp\Bitcoin\Exceptions\ScriptRuntimeException */ public function checkSignatureEncoding(BufferInterface $signature, $flags) { if ($signature->getSize() === 0) { return $this; } if (($flags & (Interpreter::VERIFY_DERSIG | Interpreter::VERIFY_LOW_S | Interpreter::VERIFY_STRICTENC)) != 0 && !$this->isValidSignatureEncoding($signature)) { throw new ScriptRuntimeException(Interpreter::VERIFY_DERSIG, 'Signature with incorrect encoding'); } else { if (($flags & Interpreter::VERIFY_LOW_S) != 0 && !$this->isLowDerSignature($signature)) { throw new ScriptRuntimeException(Interpreter::VERIFY_LOW_S, 'Signature s element was not low'); } else { if (($flags & Interpreter::VERIFY_STRICTENC) != 0 && !$this->isDefinedHashtypeSignature($signature)) { throw new ScriptRuntimeException(Interpreter::VERIFY_STRICTENC, 'Signature with invalid hashtype'); } } } return $this; }
/** * @param int $opCode * @param BufferInterface $pushData * @return bool * @throws \Exception */ public function checkMinimalPush($opCode, BufferInterface $pushData) { $pushSize = $pushData->getSize(); $binary = $pushData->getBinary(); if ($pushSize === 0) { return $opCode === Opcodes::OP_0; } elseif ($pushSize === 1) { $first = ord($binary[0]); if ($first >= 1 && $first <= 16) { return $opCode === Opcodes::OP_1 + ($first - 1); } elseif ($first === 0x81) { return $opCode === Opcodes::OP_1NEGATE; } } elseif ($pushSize <= 75) { return $opCode === $pushSize; } elseif ($pushSize <= 255) { return $opCode === Opcodes::OP_PUSHDATA1; } elseif ($pushSize <= 65535) { return $opCode === Opcodes::OP_PUSHDATA2; } return true; }