/** * @param int $type * @param Buffer $hash */ public function __construct($type, Buffer $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; }
/** * @param int|string $version * @param Buffer $prevBlock * @param Buffer $merkleRoot * @param int|string $timestamp * @param Buffer $bits * @param int|string $nonce */ public function __construct($version, Buffer $prevBlock, Buffer $merkleRoot, $timestamp, Buffer $bits, $nonce) { if (!is_numeric($version)) { throw new \InvalidArgumentException('Block header version must be numeric'); } 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'); } if (!is_numeric($timestamp)) { throw new \InvalidArgumentException('Block header timestamp must be numeric'); } if (!is_numeric($nonce)) { throw new \InvalidArgumentException('Block header nonce must be numeric'); } $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 Buffer $publicKey * @return bool */ public static function isCompressedOrUncompressed(Buffer $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; }
/** * OutPoint constructor. * @param Buffer $hashPrevOutput * @param int $nPrevOutput */ public function __construct(Buffer $hashPrevOutput, $nPrevOutput) { if ($hashPrevOutput->getSize() !== 32) { throw new \InvalidArgumentException('OutPoint: hashPrevOut must be a 32-byte Buffer'); } if (!is_numeric($nPrevOutput)) { throw new \InvalidArgumentException('OutPoint: nPrevOut must be numeric'); } $this->hashPrevOutput = $hashPrevOutput; $this->nPrevOutput = $nPrevOutput; $this->initFunctionAlias('txid', 'getTxId')->initFunctionAlias('vout', 'getVout'); }
/** * @param Buffer $entropy * @return array */ public function entropyToWords(Buffer $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 \BitWasp\Buffertools\Buffer $sig * @return bool * @throws SignatureNotCanonical */ public static function isDERSignature(Buffer $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] & pack("H*", '80'); if (ord($vAnd) === 128) { throw new SignatureNotCanonical('Signature ' . $fieldName . ' value is negative'); } if ($length > 1 && ord($val[0]) == 0x0 && !ord($val[1] & pack('H*', '80'))) { 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 Buffer $entropy * @return array * @throws \Exception */ public function entropyToWords(Buffer $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; }
/** * Encode a given hex string in base58 * * @param Buffer $binary * @return string * @throws \Exception */ public static function encode(Buffer $binary) { $size = $binary->getSize(); if ($size == 0) { 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 = $return . self::$base58chars[$rem]; } $return = strrev($return); //leading zeros for ($i = 0; $i < $size && $orig[$i] == ""; $i++) { $return = "1" . $return; } return $return; }
/** * @param Buffer $buffer * @return int */ private function parseBuffer(Buffer $buffer) { $size = $buffer->getSize(); if ($size === 0) { return '0'; } $chars = array_map(function ($binary) { return ord($binary); }, str_split($buffer->flip()->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[0] & 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(Buffer $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 Buffer $publicKey * @return PublicKeyInterface * @throws \Exception */ public function publicKeyFromBuffer(Buffer $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 $opCode * @param Buffer $pushData * @return bool * @throws \Exception */ public function checkMinimalPush($opCode, Buffer $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; }
/** * @param $opCode * @param Buffer $pushData * @return bool * @throws \Exception */ public function checkMinimalPush($opCode, Buffer $pushData) { $pushSize = $pushData->getSize(); $binary = $pushData->getBinary(); $opcodes = $this->script->getOpCodes(); if ($pushSize == 0) { return $opcodes->isOp($opCode, 'OP_0'); } elseif ($pushSize == 1 && ord($binary[0]) >= 1 && ord($binary[0]) <= 16) { return $opCode == $opcodes->getOpByName('OP_1') + (ord($binary[0]) - 1); } elseif ($pushSize == 1 && ord($binary[0]) == 0x81) { return $opcodes->isOp($opCode, 'OP_1NEGATE'); } elseif ($pushSize <= 75) { return $opCode == $pushSize; } elseif ($pushSize <= 255) { return $opcodes->isOp($opCode, 'OP_PUSHDATA1'); } elseif ($pushSize <= 65535) { return $opcodes->isOp($opCode, 'OP_PUSHDATA2'); } return true; }
/** * Push data into the stack. * * @param $data * @return $this * @throws \Exception */ public function push(Buffer $data) { $length = $data->getSize(); $parsed = new Parser(); /** Note that larger integers are serialized without flipping bits - Big endian */ if ($length < $this->opcodes->getOpByName('OP_PUSHDATA1')) { $parsed = $parsed->writeWithLength($data); } elseif ($length <= 0xff) { $parsed->writeInt(1, $this->opcodes->getOpByName('OP_PUSHDATA1'))->writeInt(1, $length, false)->writeBytes($length, $data); } elseif ($length <= 0xffff) { $parsed->writeInt(1, $this->opcodes->getOpByName('OP_PUSHDATA2'))->writeInt(2, $length, true)->writeBytes($length, $data); } else { $parsed->writeInt(1, $this->opcodes->getOpByName('OP_PUSHDATA4'))->writeInt(4, $length, true)->writeBytes($length, $data); } $this->script .= $parsed->getBuffer()->getBinary(); return $this; }