├── .gitattributes ├── BIPS.md ├── LICENCE ├── composer.json ├── phpstan.neon ├── src ├── Address │ ├── Address.php │ ├── AddressCreator.php │ ├── AddressInterface.php │ ├── Base58Address.php │ ├── Base58AddressInterface.php │ ├── BaseAddressCreator.php │ ├── Bech32AddressInterface.php │ ├── PayToPubKeyHashAddress.php │ ├── ScriptHashAddress.php │ └── SegwitAddress.php ├── Amount.php ├── Base58.php ├── Bitcoin.php ├── Block │ ├── Block.php │ ├── BlockFactory.php │ ├── BlockHeader.php │ ├── BlockHeaderFactory.php │ ├── BlockHeaderInterface.php │ ├── BlockInterface.php │ ├── FilteredBlock.php │ ├── MerkleRoot.php │ └── PartialMerkleTree.php ├── Bloom │ └── BloomFilter.php ├── Chain │ ├── BlockLocator.php │ ├── Params.php │ ├── ParamsInterface.php │ └── ProofOfWork.php ├── Collection │ ├── CollectionInterface.php │ ├── StaticBufferCollection.php │ └── StaticCollection.php ├── Crypto │ ├── EcAdapter │ │ ├── Adapter │ │ │ └── EcAdapterInterface.php │ │ ├── EcAdapterFactory.php │ │ ├── EcSerializer.php │ │ ├── Impl │ │ │ ├── PhpEcc │ │ │ │ ├── Adapter │ │ │ │ │ └── EcAdapter.php │ │ │ │ ├── Key │ │ │ │ │ ├── PrivateKey.php │ │ │ │ │ └── PublicKey.php │ │ │ │ ├── Serializer │ │ │ │ │ ├── Key │ │ │ │ │ │ ├── PrivateKeySerializer.php │ │ │ │ │ │ └── PublicKeySerializer.php │ │ │ │ │ └── Signature │ │ │ │ │ │ ├── CompactSignatureSerializer.php │ │ │ │ │ │ └── DerSignatureSerializer.php │ │ │ │ └── Signature │ │ │ │ │ ├── CompactSignature.php │ │ │ │ │ ├── CompactSignatureInterface.php │ │ │ │ │ ├── Signature.php │ │ │ │ │ └── SignatureInterface.php │ │ │ └── Secp256k1 │ │ │ │ ├── Adapter │ │ │ │ └── EcAdapter.php │ │ │ │ ├── Key │ │ │ │ ├── PrivateKey.php │ │ │ │ └── PublicKey.php │ │ │ │ ├── Serializer │ │ │ │ ├── Key │ │ │ │ │ ├── PrivateKeySerializer.php │ │ │ │ │ └── PublicKeySerializer.php │ │ │ │ └── Signature │ │ │ │ │ ├── CompactSignatureSerializer.php │ │ │ │ │ └── DerSignatureSerializer.php │ │ │ │ └── Signature │ │ │ │ ├── CompactSignature.php │ │ │ │ ├── CompactSignatureInterface.php │ │ │ │ ├── Signature.php │ │ │ │ └── SignatureInterface.php │ │ ├── Key │ │ │ ├── Key.php │ │ │ ├── KeyInterface.php │ │ │ ├── PrivateKeyInterface.php │ │ │ └── PublicKeyInterface.php │ │ ├── Serializer │ │ │ ├── Key │ │ │ │ ├── PrivateKeySerializerInterface.php │ │ │ │ └── PublicKeySerializerInterface.php │ │ │ └── Signature │ │ │ │ ├── CompactSignatureSerializerInterface.php │ │ │ │ └── DerSignatureSerializerInterface.php │ │ └── Signature │ │ │ ├── CompactSignatureInterface.php │ │ │ └── SignatureInterface.php │ ├── Hash.php │ └── Random │ │ ├── Random.php │ │ ├── RbgInterface.php │ │ └── Rfc6979.php ├── Exceptions │ ├── Base58ChecksumFailure.php │ ├── Base58InvalidCharacter.php │ ├── BlockPowError.php │ ├── BlockPrevNotFound.php │ ├── BuilderNoInputState.php │ ├── DisallowedScriptDataFactoryException.php │ ├── InvalidDerivationException.php │ ├── InvalidHashLengthException.php │ ├── InvalidNetworkParameter.php │ ├── InvalidPrivateKey.php │ ├── JsonRpcError.php │ ├── MerkleTreeEmpty.php │ ├── MissingBase58Prefix.php │ ├── MissingBech32Prefix.php │ ├── MissingBip32Prefix.php │ ├── MissingNetworkParameter.php │ ├── MissingScriptException.php │ ├── NotImplementedException.php │ ├── P2shScriptException.php │ ├── RandomBytesFailure.php │ ├── ScriptHashMismatch.php │ ├── ScriptQualificationError.php │ ├── ScriptRuntimeException.php │ ├── ScriptStackException.php │ ├── SignatureNotCanonical.php │ ├── SignerException.php │ ├── SquareRootException.php │ ├── SuperfluousScriptData.php │ ├── UnrecognizedAddressException.php │ ├── UnsupportedScript.php │ └── WitnessScriptException.php ├── Key │ ├── Deterministic │ │ ├── ElectrumKey.php │ │ ├── HdPrefix │ │ │ ├── GlobalPrefixConfig.php │ │ │ ├── NetworkConfig.php │ │ │ └── ScriptPrefix.php │ │ ├── HierarchicalKey.php │ │ ├── HierarchicalKeySequence.php │ │ ├── MultisigHD.php │ │ └── Slip132 │ │ │ ├── PrefixRegistry.php │ │ │ └── Slip132.php │ ├── Factory │ │ ├── ElectrumKeyFactory.php │ │ ├── HierarchicalKeyFactory.php │ │ ├── PrivateKeyFactory.php │ │ └── PublicKeyFactory.php │ └── KeyToScript │ │ ├── Decorator │ │ ├── P2shP2wshScriptDecorator.php │ │ ├── P2shScriptDecorator.php │ │ ├── P2wshScriptDecorator.php │ │ └── ScriptHashDecorator.php │ │ ├── Factory │ │ ├── KeyToScriptDataFactory.php │ │ ├── MultisigScriptDataFactory.php │ │ ├── P2pkScriptDataFactory.php │ │ ├── P2pkhScriptDataFactory.php │ │ └── P2wpkhScriptDataFactory.php │ │ ├── KeyToScriptHelper.php │ │ ├── ScriptAndSignData.php │ │ └── ScriptDataFactory.php ├── Locktime.php ├── Math │ └── Math.php ├── MessageSigner │ ├── MessageSigner.php │ └── SignedMessage.php ├── Mnemonic │ ├── Bip39 │ │ ├── Bip39Mnemonic.php │ │ ├── Bip39SeedGenerator.php │ │ ├── Bip39WordListInterface.php │ │ └── Wordlist │ │ │ ├── EnglishWordList.php │ │ │ └── JapaneseWordList.php │ ├── Electrum │ │ ├── ElectrumMnemonic.php │ │ ├── ElectrumWordListInterface.php │ │ └── Wordlist │ │ │ └── EnglishWordList.php │ ├── MnemonicFactory.php │ ├── MnemonicInterface.php │ ├── WordList.php │ └── WordListInterface.php ├── Network │ ├── Network.php │ ├── NetworkFactory.php │ ├── NetworkInterface.php │ ├── Networks │ │ ├── Bitcoin.php │ │ ├── BitcoinRegtest.php │ │ ├── BitcoinTestnet.php │ │ ├── Dash.php │ │ ├── DashTestnet.php │ │ ├── Dogecoin.php │ │ ├── DogecoinTestnet.php │ │ ├── Litecoin.php │ │ ├── LitecoinTestnet.php │ │ ├── Viacoin.php │ │ ├── ViacoinTestnet.php │ │ └── Zcash.php │ └── Slip132 │ │ ├── BitcoinRegistry.php │ │ └── BitcoinTestnetRegistry.php ├── Script │ ├── Classifier │ │ ├── OutputClassifier.php │ │ └── OutputData.php │ ├── Consensus │ │ ├── BitcoinConsensus.php │ │ ├── ConsensusInterface.php │ │ ├── Exception │ │ │ └── BitcoinConsensusException.php │ │ └── NativeConsensus.php │ ├── Factory │ │ ├── OutputScriptFactory.php │ │ └── ScriptCreator.php │ ├── FullyQualifiedScript.php │ ├── Interpreter │ │ ├── Checker.php │ │ ├── CheckerBase.php │ │ ├── Interpreter.php │ │ ├── InterpreterInterface.php │ │ ├── Number.php │ │ ├── Stack.php │ │ └── StackInterface.php │ ├── Opcodes.php │ ├── P2shScript.php │ ├── Parser │ │ ├── Operation.php │ │ └── Parser.php │ ├── Path │ │ ├── BranchInterpreter.php │ │ ├── LogicOpNode.php │ │ ├── ParsedScript.php │ │ ├── PathTracer.php │ │ └── ScriptBranch.php │ ├── Script.php │ ├── ScriptFactory.php │ ├── ScriptInfo │ │ ├── Multisig.php │ │ ├── PayToPubkey.php │ │ └── PayToPubkeyHash.php │ ├── ScriptInterface.php │ ├── ScriptType.php │ ├── ScriptWitness.php │ ├── ScriptWitnessInterface.php │ ├── WitnessProgram.php │ ├── WitnessScript.php │ └── functions.php ├── Serializable.php ├── SerializableInterface.php ├── Serializer │ ├── Block │ │ ├── BitcoindBlockSerializer.php │ │ ├── BlockHeaderSerializer.php │ │ ├── BlockSerializer.php │ │ ├── BlockSerializerInterface.php │ │ ├── FilteredBlockSerializer.php │ │ └── PartialMerkleTreeSerializer.php │ ├── Bloom │ │ └── BloomFilterSerializer.php │ ├── Chain │ │ └── BlockLocatorSerializer.php │ ├── Key │ │ ├── HierarchicalKey │ │ │ ├── Base58ExtendedKeySerializer.php │ │ │ ├── ExtendedKeySerializer.php │ │ │ ├── RawExtendedKeySerializer.php │ │ │ └── RawKeyParams.php │ │ └── PrivateKey │ │ │ └── WifPrivateKeySerializer.php │ ├── MessageSigner │ │ └── SignedMessageSerializer.php │ ├── Script │ │ └── ScriptWitnessSerializer.php │ ├── Signature │ │ └── TransactionSignatureSerializer.php │ ├── Transaction │ │ ├── OutPointSerializer.php │ │ ├── OutPointSerializerInterface.php │ │ ├── TransactionInputSerializer.php │ │ ├── TransactionOutputSerializer.php │ │ ├── TransactionSerializer.php │ │ └── TransactionSerializerInterface.php │ └── Types.php ├── Signature │ ├── SignatureFactory.php │ ├── SignatureSort.php │ ├── SignatureSortInterface.php │ ├── TransactionSignature.php │ ├── TransactionSignatureFactory.php │ └── TransactionSignatureInterface.php ├── Transaction │ ├── Bip69 │ │ └── Bip69.php │ ├── Factory │ │ ├── Checker │ │ │ ├── CheckerCreator.php │ │ │ └── CheckerCreatorBase.php │ │ ├── Checksig.php │ │ ├── Conditional.php │ │ ├── InputSigner.php │ │ ├── InputSignerInterface.php │ │ ├── ScriptInfo │ │ │ ├── CheckLocktimeVerify.php │ │ │ └── CheckSequenceVerify.php │ │ ├── SigValues.php │ │ ├── SignData.php │ │ ├── Signer.php │ │ ├── TimeLock.php │ │ └── TxBuilder.php │ ├── Mutator │ │ ├── AbstractCollectionMutator.php │ │ ├── InputCollectionMutator.php │ │ ├── InputMutator.php │ │ ├── OutputCollectionMutator.php │ │ ├── OutputMutator.php │ │ ├── TxMutator.php │ │ └── WitnessCollectionMutator.php │ ├── OutPoint.php │ ├── OutPointInterface.php │ ├── SignatureHash │ │ ├── Hasher.php │ │ ├── SigHash.php │ │ ├── SigHashInterface.php │ │ ├── TxSigHashSerializer.php │ │ └── V1Hasher.php │ ├── Transaction.php │ ├── TransactionFactory.php │ ├── TransactionInput.php │ ├── TransactionInputInterface.php │ ├── TransactionInterface.php │ ├── TransactionOutput.php │ └── TransactionOutputInterface.php ├── Uri.php ├── Util │ └── IntRange.php └── Utxo │ ├── Utxo.php │ └── UtxoInterface.php └── validate_examples.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | /doc export-ignore 2 | /examples export-ignore 3 | /stubs export-ignore 4 | /tests export-ignore 5 | /tests-rpc export-ignore 6 | /travis export-ignore 7 | /.gitignore export-ignore 8 | /.scrutinizer.yml export-ignore 9 | /.travis.yml export-ignore 10 | /phpunit.xml export-ignore 11 | /phpunit.rpc.xml export-ignore 12 | /CONTRIBUTING.md export-ignore 13 | /README.md export-ignore 14 | /Makefile export-ignore 15 | -------------------------------------------------------------------------------- /BIPS.md: -------------------------------------------------------------------------------- 1 | 2 | ## Implemented BIPs 3 | 4 | This page lists BIPs implemented by the core library. 5 | 6 | Some BIPs are specific to the network layer - for these see [Bitcoin P2P](https://github.com/Bit-Wasp/bitcoin-p2p-php) 7 | 8 | - [BIP 11](https://github.com/bitcoin/bips/blob/master/bip-0011.mediawiki) - M of N standard transactions 9 | - [BIP 13](https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki) - Pay to Script Hash address format 10 | - [BIP 14](https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki) - Protocol Version and User Agent 11 | - [BIP 16](https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki) - Pay to Script Hash 12 | - [BIP 21](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki) - Bitcoin URI's 13 | - [BIP 32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) - Hierarchical Deterministic Wallets 14 | - [BIP 37](https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki) - Bloom Filtering 15 | - [BIP 39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) - Mnemonic code for generating deterministic keys 16 | - [BIP 65](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki) - OP_CHECKLOCKTIMEVERIFY 17 | - [BIP 66](https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki) - Strict DER Signatures 18 | - [BIP 67](https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki) - Deterministic P2SH multi-signature addresses 19 | - [BIP 69](https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki) - Lexicographical Indexing of Transaction Inputs and Outputs 20 | - [BIP 70](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki) - Payment Protocol 21 | - [BIP 72](https://github.com/bitcoin/bips/blob/master/bip-0072.mediawiki) - Payment protocol URIs 22 | - [BIP 112](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki) - OP_CHECKSEQUENCEVERIFY 23 | - [BIP 141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki) - Segregated Witness (Consensus layer) 24 | - [BIP 143](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki) - Transaction Signature Verification for Version 0 Witness Program 25 | - [BIP 146](https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki) - Dealing with signature encoding malleability 26 | - [BIP 147](https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki) - Dealing with dummy stack element malleability 27 | - [BIP 173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) - Bech32 and segwit address encoding 28 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bitwasp/bitcoin", 3 | "description": "PHP Bitcoin library with functions for transactions, signatures, serialization, Random/Deterministic ECDSA keys, blocks, RPC bindings", 4 | "type": "library", 5 | "homepage": "https://github.com/bit-wasp/bitcoin-php", 6 | "license": "Unlicense", 7 | "authors": [ 8 | { 9 | "name": "Thomas Kerin", 10 | "homepage": "https://thomaskerin.io", 11 | "role": "Author" 12 | } 13 | ], 14 | "autoload": { 15 | "psr-4": { 16 | "BitWasp\\Bitcoin\\": "src/" 17 | }, 18 | "files": ["src/Script/functions.php"] 19 | }, 20 | "autoload-dev": { 21 | "psr-4": { 22 | "BitWasp\\Bitcoin\\Tests\\": "tests/" 23 | } 24 | }, 25 | "require": { 26 | "php-64bit": ">=7.0", 27 | "pleonasm/merkle-tree": "1.0.0", 28 | "composer/semver": "^1.4.0|^3.2.0", 29 | "lastguest/murmurhash": "v2.0.0", 30 | "mdanter/ecc": "^0.5.0", 31 | "bitwasp/buffertools": "^0.5.0", 32 | "bitwasp/bech32": "^0.0.1" 33 | }, 34 | "suggest": { 35 | "ext-secp256k1": "The secp256k1 library for fast and safe elliptic curve operations", 36 | "ext-bitcoinconsensus": "The bitcoinconsensus library for safest possible script verification" 37 | }, 38 | "require-dev": { 39 | "ext-json": "*", 40 | "phpunit/phpunit": "^5.4.0", 41 | "squizlabs/php_codesniffer": "^2.0.0", 42 | "nbobtc/bitcoind-php": "v2.0.2", 43 | "bitwasp/secp256k1-php": "^v0.2.0", 44 | "bitwasp/bitcoinconsensus": "v3.0.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | excludes_analyse: 3 | - src/Collection 4 | -------------------------------------------------------------------------------- /src/Address/Address.php: -------------------------------------------------------------------------------- 1 | hash = $hash; 26 | } 27 | 28 | /** 29 | * @return BufferInterface 30 | */ 31 | public function getHash(): BufferInterface 32 | { 33 | return $this->hash; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Address/AddressInterface.php: -------------------------------------------------------------------------------- 1 | getPrefixByte($network) . $this->getHash()->getBinary()); 22 | return Base58::encodeCheck($payload); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Address/Base58AddressInterface.php: -------------------------------------------------------------------------------- 1 | getSize() !== 20) { 22 | throw new \RuntimeException("P2PKH address hash should be 20 bytes"); 23 | } 24 | 25 | parent::__construct($data); 26 | } 27 | 28 | /** 29 | * @param NetworkInterface $network 30 | * @return string 31 | */ 32 | public function getPrefixByte(NetworkInterface $network = null): string 33 | { 34 | $network = $network ?: Bitcoin::getNetwork(); 35 | return pack("H*", $network->getAddressByte()); 36 | } 37 | 38 | /** 39 | * @return ScriptInterface 40 | */ 41 | public function getScriptPubKey(): ScriptInterface 42 | { 43 | return ScriptFactory::scriptPubKey()->payToPubKeyHash($this->getHash()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Address/ScriptHashAddress.php: -------------------------------------------------------------------------------- 1 | getSize() !== 20) { 22 | throw new \RuntimeException("P2SH address hash should be 20 bytes"); 23 | } 24 | 25 | parent::__construct($data); 26 | } 27 | 28 | /** 29 | * @param NetworkInterface $network 30 | * @return string 31 | */ 32 | public function getPrefixByte(NetworkInterface $network = null): string 33 | { 34 | $network = $network ?: Bitcoin::getNetwork(); 35 | return pack("H*", $network->getP2shByte()); 36 | } 37 | 38 | /** 39 | * @return ScriptInterface 40 | */ 41 | public function getScriptPubKey(): ScriptInterface 42 | { 43 | return ScriptFactory::scriptPubKey()->payToScriptHash($this->getHash()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Address/SegwitAddress.php: -------------------------------------------------------------------------------- 1 | witnessProgram = $witnessProgram; 26 | 27 | parent::__construct($witnessProgram->getProgram()); 28 | } 29 | 30 | /** 31 | * @param NetworkInterface|null $network 32 | * @return string 33 | */ 34 | public function getHRP(NetworkInterface $network = null): string 35 | { 36 | $network = $network ?: Bitcoin::getNetwork(); 37 | return $network->getSegwitBech32Prefix(); 38 | } 39 | 40 | /** 41 | * @return WitnessProgram 42 | */ 43 | public function getWitnessProgram(): WitnessProgram 44 | { 45 | return $this->witnessProgram; 46 | } 47 | 48 | /** 49 | * @return ScriptInterface 50 | */ 51 | public function getScriptPubKey(): ScriptInterface 52 | { 53 | return $this->witnessProgram->getScript(); 54 | } 55 | 56 | /** 57 | * @param NetworkInterface|null $network 58 | * @return string 59 | */ 60 | public function getAddress(NetworkInterface $network = null): string 61 | { 62 | $network = $network ?: Bitcoin::getNetwork(); 63 | 64 | return \BitWasp\Bech32\encodeSegwit($network->getSegwitBech32Prefix(), $this->witnessProgram->getVersion(), $this->witnessProgram->getProgram()->getBinary()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Amount.php: -------------------------------------------------------------------------------- 1 | parse($buffer); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Block/BlockHeaderFactory.php: -------------------------------------------------------------------------------- 1 | parse($buffer); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Block/BlockHeaderInterface.php: -------------------------------------------------------------------------------- 1 | header = $header; 32 | $this->partialTree = $merkleTree; 33 | } 34 | 35 | /** 36 | * @return BlockHeaderInterface 37 | */ 38 | public function getHeader(): BlockHeaderInterface 39 | { 40 | return $this->header; 41 | } 42 | 43 | /** 44 | * @return PartialMerkleTree 45 | */ 46 | public function getPartialTree(): PartialMerkleTree 47 | { 48 | return $this->partialTree; 49 | } 50 | 51 | /** 52 | * @return BufferInterface 53 | */ 54 | public function getBuffer(): BufferInterface 55 | { 56 | return (new FilteredBlockSerializer(new BlockHeaderSerializer(), new PartialMerkleTreeSerializer()))->serialize($this); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Block/MerkleRoot.php: -------------------------------------------------------------------------------- 1 | math = $math; 40 | $this->transactions = $txCollection; 41 | } 42 | 43 | /** 44 | * @param callable|null $hashFunction 45 | * @return BufferInterface 46 | * @throws MerkleTreeEmpty 47 | */ 48 | public function calculateHash(callable $hashFunction = null): BufferInterface 49 | { 50 | if ($this->lastHash instanceof BufferInterface) { 51 | return $this->lastHash; 52 | } 53 | 54 | $hashFxn = $hashFunction ?: function ($value) { 55 | return hash('sha256', hash('sha256', $value, true), true); 56 | }; 57 | 58 | $txCount = count($this->transactions); 59 | if ($txCount === 0) { 60 | // TODO: Probably necessary. Should always have a coinbase at least. 61 | throw new MerkleTreeEmpty('Cannot compute Merkle root of an empty tree'); 62 | } 63 | 64 | if ($txCount === 1) { 65 | $binary = $hashFxn($this->transactions[0]->getBinary()); 66 | } else { 67 | // Create a fixed size Merkle Tree 68 | $tree = new FixedSizeTree($txCount + ($txCount % 2), $hashFxn); 69 | 70 | // Compute hash of each transaction 71 | $last = ''; 72 | foreach ($this->transactions as $i => $transaction) { 73 | $last = $transaction->getBinary(); 74 | $tree->set($i, $last); 75 | } 76 | 77 | // Check if we need to repeat the last hash (odd number of transactions) 78 | if (!($txCount % 2 === 0)) { 79 | $tree->set($txCount, $last); 80 | } 81 | 82 | $binary = $tree->hash(); 83 | } 84 | 85 | $this->lastHash = (new Buffer($binary))->flip(); 86 | return $this->lastHash; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Chain/BlockLocator.php: -------------------------------------------------------------------------------- 1 | addHash($hash); 31 | } 32 | 33 | $this->hashStop = $hashStop; 34 | } 35 | 36 | /** 37 | * @param BufferInterface $hash 38 | */ 39 | private function addHash(BufferInterface $hash) 40 | { 41 | $this->hashes[] = $hash; 42 | } 43 | 44 | /** 45 | * @return BufferInterface[] 46 | */ 47 | public function getHashes(): array 48 | { 49 | return $this->hashes; 50 | } 51 | 52 | /** 53 | * @return BufferInterface 54 | */ 55 | public function getHashStop(): BufferInterface 56 | { 57 | return $this->hashStop; 58 | } 59 | 60 | /** 61 | * @return BufferInterface 62 | */ 63 | public function getBuffer(): BufferInterface 64 | { 65 | return (new BlockLocatorSerializer())->serialize($this); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Chain/ParamsInterface.php: -------------------------------------------------------------------------------- 1 | set = $values; 31 | } 32 | 33 | /** 34 | * @return BufferInterface 35 | */ 36 | public function bottom(): BufferInterface 37 | { 38 | return parent::bottom(); 39 | } 40 | 41 | /** 42 | * @return BufferInterface 43 | */ 44 | public function top(): BufferInterface 45 | { 46 | return parent::top(); 47 | } 48 | 49 | /** 50 | * @return BufferInterface 51 | */ 52 | public function current(): BufferInterface 53 | { 54 | return $this->set[$this->position]; 55 | } 56 | 57 | /** 58 | * @param int $offset 59 | * @return BufferInterface 60 | */ 61 | public function offsetGet($offset) 62 | { 63 | if (!array_key_exists($offset, $this->set)) { 64 | throw new \OutOfRangeException('No offset found'); 65 | } 66 | 67 | return $this->set[$offset]; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Crypto/EcAdapter/Adapter/EcAdapterInterface.php: -------------------------------------------------------------------------------- 1 | ecAdapter = $ecAdapter; 27 | } 28 | 29 | /** 30 | * @param PrivateKeyInterface $privateKey 31 | * @return BufferInterface 32 | */ 33 | public function serialize(PrivateKeyInterface $privateKey): BufferInterface 34 | { 35 | return Buffer::int(gmp_strval($privateKey->getSecret(), 10), 32); 36 | } 37 | 38 | /** 39 | * @param Parser $parser 40 | * @param bool $compressed 41 | * @return PrivateKeyInterface 42 | * @throws \Exception 43 | */ 44 | public function fromParser(Parser $parser, bool $compressed): PrivateKeyInterface 45 | { 46 | return $this->ecAdapter->getPrivateKey($parser->readBytes(32)->getGmp(), $compressed); 47 | } 48 | 49 | /** 50 | * @param BufferInterface $buffer 51 | * @param bool $compressed 52 | * @return PrivateKeyInterface 53 | * @throws \Exception 54 | */ 55 | public function parse(BufferInterface $buffer, bool $compressed): PrivateKeyInterface 56 | { 57 | return $this->fromParser(new Parser($buffer), $compressed); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Crypto/EcAdapter/Impl/PhpEcc/Signature/CompactSignature.php: -------------------------------------------------------------------------------- 1 | ecAdapter = $adapter; 38 | $this->recid = $recid; 39 | $this->compressed = $compressed; 40 | parent::__construct($adapter, $r, $s); 41 | } 42 | 43 | /** 44 | * @return Signature 45 | */ 46 | public function convert(): Signature 47 | { 48 | return new Signature($this->ecAdapter, $this->getR(), $this->getS()); 49 | } 50 | 51 | /** 52 | * @return int 53 | */ 54 | public function getRecoveryId(): int 55 | { 56 | return $this->recid; 57 | } 58 | 59 | /** 60 | * @return bool 61 | */ 62 | public function isCompressed(): bool 63 | { 64 | return $this->compressed; 65 | } 66 | 67 | /** 68 | * @return int 69 | */ 70 | public function getFlags(): int 71 | { 72 | return $this->getRecoveryId() + 27 + ($this->isCompressed() ? 4 : 0); 73 | } 74 | 75 | /** 76 | * @return BufferInterface 77 | */ 78 | public function getBuffer(): BufferInterface 79 | { 80 | return (new CompactSignatureSerializer($this->ecAdapter))->serialize($this); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Crypto/EcAdapter/Impl/PhpEcc/Signature/CompactSignatureInterface.php: -------------------------------------------------------------------------------- 1 | ecAdapter = $ecAdapter; 38 | $this->r = $r; 39 | $this->s = $s; 40 | } 41 | 42 | /** 43 | * @inheritdoc 44 | * @see SignatureInterface::getR() 45 | */ 46 | public function getR(): \GMP 47 | { 48 | return $this->r; 49 | } 50 | 51 | /** 52 | * @inheritdoc 53 | * @see SignatureInterface::getS() 54 | */ 55 | public function getS(): \GMP 56 | { 57 | return $this->s; 58 | } 59 | 60 | /** 61 | * @param Signature $signature 62 | * @return bool 63 | */ 64 | public function doEquals(Signature $signature): bool 65 | { 66 | $math = $this->ecAdapter->getMath(); 67 | return $math->equals($this->getR(), $signature->getR()) 68 | && $math->equals($this->getS(), $signature->getS()); 69 | } 70 | 71 | /** 72 | * @param SignatureInterface $signature 73 | * @return bool 74 | */ 75 | public function equals(SignatureInterface $signature): bool 76 | { 77 | /** @var Signature $signature */ 78 | return $this->doEquals($signature); 79 | } 80 | 81 | /** 82 | * @return \BitWasp\Buffertools\BufferInterface 83 | */ 84 | public function getBuffer(): BufferInterface 85 | { 86 | return (new DerSignatureSerializer($this->ecAdapter))->serialize($this); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Crypto/EcAdapter/Impl/PhpEcc/Signature/SignatureInterface.php: -------------------------------------------------------------------------------- 1 | ecAdapter = $ecAdapter; 31 | } 32 | 33 | /** 34 | * @param PrivateKey $privateKey 35 | * @return BufferInterface 36 | */ 37 | private function doSerialize(PrivateKey $privateKey): BufferInterface 38 | { 39 | return new Buffer($privateKey->getSecretBinary(), 32); 40 | } 41 | 42 | /** 43 | * @param PrivateKeyInterface $privateKey 44 | * @return BufferInterface 45 | */ 46 | public function serialize(PrivateKeyInterface $privateKey): BufferInterface 47 | { 48 | /** @var PrivateKey $privateKey */ 49 | return $this->doSerialize($privateKey); 50 | } 51 | 52 | /** 53 | * @param Parser $parser 54 | * @param bool $compressed 55 | * @return PrivateKeyInterface 56 | * @throws \Exception 57 | */ 58 | public function fromParser(Parser $parser, bool $compressed): PrivateKeyInterface 59 | { 60 | return $this->ecAdapter->getPrivateKey($parser->readBytes(32)->getGmp(), $compressed); 61 | } 62 | 63 | /** 64 | * @param BufferInterface $data 65 | * @param bool $compressed 66 | * @return PrivateKeyInterface 67 | * @throws \Exception 68 | */ 69 | public function parse(BufferInterface $data, bool $compressed): PrivateKeyInterface 70 | { 71 | return $this->fromParser(new Parser($data), $compressed); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Crypto/EcAdapter/Impl/Secp256k1/Serializer/Key/PublicKeySerializer.php: -------------------------------------------------------------------------------- 1 | ecAdapter = $ecAdapter; 28 | } 29 | 30 | /** 31 | * @param PublicKey $publicKey 32 | * @return BufferInterface 33 | */ 34 | private function doSerialize(PublicKey $publicKey) 35 | { 36 | $serialized = ''; 37 | $isCompressed = $publicKey->isCompressed(); 38 | if (!secp256k1_ec_pubkey_serialize( 39 | $this->ecAdapter->getContext(), 40 | $serialized, 41 | $publicKey->getResource(), 42 | $isCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED 43 | )) { 44 | throw new \RuntimeException('Secp256k1: Failed to serialize public key'); 45 | } 46 | 47 | return new Buffer( 48 | $serialized, 49 | $isCompressed ? PublicKey::LENGTH_COMPRESSED : PublicKey::LENGTH_UNCOMPRESSED 50 | ); 51 | } 52 | 53 | /** 54 | * @param PublicKeyInterface $publicKey 55 | * @return BufferInterface 56 | */ 57 | public function serialize(PublicKeyInterface $publicKey): BufferInterface 58 | { 59 | /** @var PublicKey $publicKey */ 60 | return $this->doSerialize($publicKey); 61 | } 62 | 63 | /** 64 | * @param BufferInterface $buffer 65 | * @return PublicKeyInterface 66 | */ 67 | public function parse(BufferInterface $buffer): PublicKeyInterface 68 | { 69 | $binary = $buffer->getBinary(); 70 | $pubkey_t = null; 71 | if (!secp256k1_ec_pubkey_parse($this->ecAdapter->getContext(), $pubkey_t, $binary)) { 72 | throw new \RuntimeException('Secp256k1 failed to parse public key'); 73 | } 74 | /** @var resource $pubkey_t */ 75 | return new PublicKey( 76 | $this->ecAdapter, 77 | $pubkey_t, 78 | $buffer->getSize() === 33 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Crypto/EcAdapter/Impl/Secp256k1/Signature/CompactSignatureInterface.php: -------------------------------------------------------------------------------- 1 | getPublicKey(); 35 | } else { 36 | $publicKey = $this; 37 | } 38 | 39 | if (null === $this->pubKeyHash) { 40 | $this->pubKeyHash = Hash::sha256ripe160($serializer ? $serializer->serialize($publicKey) : $publicKey->getBuffer()); 41 | } 42 | 43 | return $this->pubKeyHash; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Crypto/EcAdapter/Key/KeyInterface.php: -------------------------------------------------------------------------------- 1 | getMath(), $ecAdapter->getGenerator(), gmp_init($privateKey->getInt(), 10)); 41 | $this->ecAdapter = $ecAdapter; 42 | $this->hmac = RandomGeneratorFactory::getHmacRandomGenerator($mdPk, gmp_init($messageHash->getInt(), 10), $algo); 43 | } 44 | 45 | /** 46 | * @param int $bytes 47 | * @return BufferInterface 48 | */ 49 | public function bytes(int $bytes): BufferInterface 50 | { 51 | $integer = $this->hmac->generate($this->ecAdapter->getOrder()); 52 | return Buffer::int(gmp_strval($integer, 10), $bytes); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Exceptions/Base58ChecksumFailure.php: -------------------------------------------------------------------------------- 1 | failureFlag = $failureFlag; 19 | parent::__construct($message); 20 | } 21 | 22 | /** 23 | * var int|string 24 | */ 25 | public function getFailureFlag() 26 | { 27 | return $this->failureFlag; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Exceptions/ScriptStackException.php: -------------------------------------------------------------------------------- 1 | getNetwork()); 28 | if (array_key_exists($networkClass, $this->networkConfigs)) { 29 | throw new \InvalidArgumentException("multiple configs for network"); 30 | } 31 | 32 | $this->networkConfigs[$networkClass] = $networkPrefixConfig; 33 | } 34 | } 35 | 36 | /** 37 | * @param NetworkInterface $network 38 | * @return NetworkConfig 39 | */ 40 | public function getNetworkConfig(NetworkInterface $network): NetworkConfig 41 | { 42 | $class = get_class($network); 43 | if (!array_key_exists($class, $this->networkConfigs)) { 44 | throw new \InvalidArgumentException("Network not registered with GlobalHdPrefixConfig"); 45 | } 46 | 47 | return $this->networkConfigs[$class]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Key/Deterministic/HdPrefix/ScriptPrefix.php: -------------------------------------------------------------------------------- 1 | scriptDataFactory = $scriptDataFactory; 52 | $this->publicPrefix = $publicPrefix; 53 | $this->privatePrefix = $privatePrefix; 54 | } 55 | 56 | /** 57 | * @return string 58 | */ 59 | public function getPrivatePrefix(): string 60 | { 61 | return $this->privatePrefix; 62 | } 63 | 64 | /** 65 | * @return string 66 | */ 67 | public function getPublicPrefix(): string 68 | { 69 | return $this->publicPrefix; 70 | } 71 | 72 | /** 73 | * @return ScriptDataFactory 74 | */ 75 | public function getScriptDataFactory(): ScriptDataFactory 76 | { 77 | return $this->scriptDataFactory; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Key/Deterministic/Slip132/PrefixRegistry.php: -------------------------------------------------------------------------------- 1 | $prefixes) { 21 | if (!is_string($scriptType)) { 22 | throw new \InvalidArgumentException("Expecting script type as key"); 23 | } 24 | if (count($prefixes) !== 2) { 25 | throw new \InvalidArgumentException("Expecting two BIP32 prefixes"); 26 | } 27 | // private, public 28 | if (strlen($prefixes[0]) !== 8 || !ctype_xdigit($prefixes[0])) { 29 | throw new \InvalidArgumentException("Invalid private prefix"); 30 | } 31 | if (strlen($prefixes[1]) !== 8 || !ctype_xdigit($prefixes[1])) { 32 | throw new \InvalidArgumentException("Invalid public prefix"); 33 | } 34 | } 35 | $this->registry = $registry; 36 | } 37 | 38 | /** 39 | * @param string $scriptType 40 | * @return array 41 | */ 42 | public function getPrefixes($scriptType): array 43 | { 44 | if (!array_key_exists($scriptType, $this->registry)) { 45 | throw new \InvalidArgumentException("Unknown script type"); 46 | } 47 | return $this->registry[$scriptType]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Key/Factory/PublicKeyFactory.php: -------------------------------------------------------------------------------- 1 | serializer = EcSerializer::getSerializer(PublicKeySerializerInterface::class, true, $ecAdapter); 30 | } 31 | 32 | /** 33 | * @param string $hex 34 | * @return PublicKeyInterface 35 | * @throws \Exception 36 | */ 37 | public function fromHex(string $hex): PublicKeyInterface 38 | { 39 | return $this->fromBuffer(Buffer::hex($hex)); 40 | } 41 | 42 | /** 43 | * @param BufferInterface $buffer 44 | * @return PublicKeyInterface 45 | */ 46 | public function fromBuffer(BufferInterface $buffer): PublicKeyInterface 47 | { 48 | return $this->serializer->parse($buffer); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Key/KeyToScript/Decorator/P2shP2wshScriptDecorator.php: -------------------------------------------------------------------------------- 1 | scriptDataFactory->convertKey(...$keys)->getScriptPubKey()); 39 | $redeemScript = new P2shScript($witnessScript); 40 | return new ScriptAndSignData( 41 | $redeemScript->getOutputScript(), 42 | (new SignData()) 43 | ->p2sh($redeemScript) 44 | ->p2wsh($witnessScript) 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Key/KeyToScript/Decorator/P2shScriptDecorator.php: -------------------------------------------------------------------------------- 1 | scriptDataFactory->convertKey(...$keys)->getScriptPubKey()); 38 | return new ScriptAndSignData( 39 | $redeemScript->getOutputScript(), 40 | (new SignData()) 41 | ->p2sh($redeemScript) 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Key/KeyToScript/Decorator/P2wshScriptDecorator.php: -------------------------------------------------------------------------------- 1 | scriptDataFactory->convertKey(...$keys)->getScriptPubKey()); 37 | return new ScriptAndSignData( 38 | $witnessScript->getOutputScript(), 39 | (new SignData()) 40 | ->p2wsh($witnessScript) 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Key/KeyToScript/Decorator/ScriptHashDecorator.php: -------------------------------------------------------------------------------- 1 | getScriptType(), $this->allowedScriptTypes, true)) { 31 | throw new DisallowedScriptDataFactoryException("Unsupported key-to-script factory for this script-hash type."); 32 | } 33 | $this->scriptDataFactory = $scriptDataFactory; 34 | } 35 | 36 | /** 37 | * @return string 38 | */ 39 | public function getScriptType(): string 40 | { 41 | return sprintf("%s|%s", $this->decorateType, $this->scriptDataFactory->getScriptType()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Key/KeyToScript/Factory/KeyToScriptDataFactory.php: -------------------------------------------------------------------------------- 1 | pubKeySerializer = $pubKeySerializer; 33 | } 34 | 35 | /** 36 | * @param PublicKeyInterface ...$keys 37 | * @return ScriptAndSignData 38 | */ 39 | abstract protected function convertKeyToScriptData(PublicKeyInterface ...$keys): ScriptAndSignData; 40 | 41 | /** 42 | * @param KeyInterface ...$keys 43 | * @return ScriptAndSignData 44 | */ 45 | public function convertKey(KeyInterface... $keys): ScriptAndSignData 46 | { 47 | $pubs = []; 48 | foreach ($keys as $key) { 49 | if ($key instanceof PrivateKeyInterface) { 50 | $key = $key->getPublicKey(); 51 | } 52 | $pubs[] = $key; 53 | } 54 | 55 | return $this->convertKeyToScriptData(...$pubs); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Key/KeyToScript/Factory/MultisigScriptDataFactory.php: -------------------------------------------------------------------------------- 1 | numSigners = $numSigners; 34 | $this->numKeys = $numKeys; 35 | $this->sortKeys = $sortKeys; 36 | parent::__construct($pubKeySerializer); 37 | } 38 | 39 | /** 40 | * @return string 41 | */ 42 | public function getScriptType(): string 43 | { 44 | return ScriptType::MULTISIG; 45 | } 46 | 47 | /** 48 | * @param PublicKeyInterface ...$keys 49 | * @return ScriptAndSignData 50 | */ 51 | protected function convertKeyToScriptData(PublicKeyInterface ...$keys): ScriptAndSignData 52 | { 53 | if (count($keys) !== $this->numKeys) { 54 | throw new \InvalidArgumentException("Incorrect number of keys"); 55 | } 56 | 57 | $keyBuffers = []; 58 | for ($i = 0; $i < $this->numKeys; $i++) { 59 | $keyBuffers[] = $this->pubKeySerializer->serialize($keys[$i]); 60 | } 61 | 62 | return new ScriptAndSignData( 63 | ScriptFactory::scriptPubKey()->multisigKeyBuffers($this->numSigners, $keyBuffers, $this->sortKeys), 64 | new SignData() 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Key/KeyToScript/Factory/P2pkScriptDataFactory.php: -------------------------------------------------------------------------------- 1 | p2pk($keys[0]), 34 | new SignData() 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Key/KeyToScript/Factory/P2pkhScriptDataFactory.php: -------------------------------------------------------------------------------- 1 | p2pkh($keys[0]->getPubKeyHash($this->pubKeySerializer)), 34 | new SignData() 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Key/KeyToScript/Factory/P2wpkhScriptDataFactory.php: -------------------------------------------------------------------------------- 1 | isCompressed()) { 33 | throw new \InvalidArgumentException("Cannot create P2WPKH address for non-compressed public key"); 34 | } 35 | return new ScriptAndSignData( 36 | ScriptFactory::scriptPubKey()->p2wkh($keys[0]->getPubKeyHash($this->pubKeySerializer)), 37 | new SignData() 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Key/KeyToScript/ScriptAndSignData.php: -------------------------------------------------------------------------------- 1 | scriptPubKey = $scriptPubKey; 32 | $this->signData = $signData; 33 | } 34 | 35 | /** 36 | * @return ScriptInterface 37 | */ 38 | public function getScriptPubKey(): ScriptInterface 39 | { 40 | return $this->scriptPubKey; 41 | } 42 | 43 | /** 44 | * @param BaseAddressCreator $creator 45 | * @return Address 46 | */ 47 | public function getAddress(BaseAddressCreator $creator): Address 48 | { 49 | return $creator->fromOutputScript($this->scriptPubKey); 50 | } 51 | 52 | /** 53 | * @return SignData 54 | */ 55 | public function getSignData(): SignData 56 | { 57 | return $this->signData; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Key/KeyToScript/ScriptDataFactory.php: -------------------------------------------------------------------------------- 1 | message = $message; 32 | $this->compactSignature = $signature; 33 | } 34 | 35 | /** 36 | * @return string 37 | */ 38 | public function getMessage(): string 39 | { 40 | return $this->message; 41 | } 42 | 43 | /** 44 | * @return CompactSignatureInterface 45 | */ 46 | public function getCompactSignature(): CompactSignatureInterface 47 | { 48 | return $this->compactSignature; 49 | } 50 | 51 | /** 52 | * @return \BitWasp\Buffertools\BufferInterface 53 | */ 54 | public function getBuffer() 55 | { 56 | $serializer = new SignedMessageSerializer( 57 | EcSerializer::getSerializer(CompactSignatureSerializerInterface::class) 58 | ); 59 | return $serializer->serialize($this); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Mnemonic/Bip39/Bip39SeedGenerator.php: -------------------------------------------------------------------------------- 1 | normalize($mnemonic), 42 | $this->normalize("mnemonic{$passphrase}"), 43 | 2048, 44 | 64 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Mnemonic/Bip39/Bip39WordListInterface.php: -------------------------------------------------------------------------------- 1 | getWords(); 16 | if (!isset($words[$index])) { 17 | throw new \InvalidArgumentException(__CLASS__ . " does not contain a word for index [{$index}]"); 18 | } 19 | 20 | return $words[$index]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Mnemonic/WordListInterface.php: -------------------------------------------------------------------------------- 1 | "00", 16 | self::BASE58_ADDRESS_P2SH => "05", 17 | self::BASE58_WIF => "80", 18 | ]; 19 | 20 | /** 21 | * {@inheritdoc} 22 | * @see Network::$bech32PrefixMap 23 | */ 24 | protected $bech32PrefixMap = [ 25 | self::BECH32_PREFIX_SEGWIT => "bc", 26 | ]; 27 | 28 | /** 29 | * {@inheritdoc} 30 | * @see Network::$bip32PrefixMap 31 | */ 32 | protected $bip32PrefixMap = [ 33 | self::BIP32_PREFIX_XPUB => "0488b21e", 34 | self::BIP32_PREFIX_XPRV => "0488ade4", 35 | ]; 36 | 37 | /** 38 | * {@inheritdoc} 39 | * @see Network::$bip32ScriptTypeMap 40 | */ 41 | protected $bip32ScriptTypeMap = [ 42 | self::BIP32_PREFIX_XPUB => ScriptType::P2PKH, 43 | self::BIP32_PREFIX_XPRV => ScriptType::P2PKH, 44 | ]; 45 | 46 | /** 47 | * {@inheritdoc} 48 | * @see Network::$signedMessagePrefix 49 | */ 50 | protected $signedMessagePrefix = "Bitcoin Signed Message"; 51 | 52 | /** 53 | * {@inheritdoc} 54 | * @see Network::$p2pMagic 55 | */ 56 | protected $p2pMagic = "d9b4bef9"; 57 | } 58 | -------------------------------------------------------------------------------- /src/Network/Networks/BitcoinRegtest.php: -------------------------------------------------------------------------------- 1 | "6f", 16 | self::BASE58_ADDRESS_P2SH => "c4", 17 | self::BASE58_WIF => "ef", 18 | ]; 19 | 20 | /** 21 | * {@inheritdoc} 22 | * @see Network::$bech32PrefixMap 23 | */ 24 | protected $bech32PrefixMap = [ 25 | self::BECH32_PREFIX_SEGWIT => "tb", 26 | ]; 27 | 28 | /** 29 | * {@inheritdoc} 30 | * @see Network::$bip32PrefixMap 31 | */ 32 | protected $bip32PrefixMap = [ 33 | self::BIP32_PREFIX_XPUB => "043587cf", 34 | self::BIP32_PREFIX_XPRV => "04358394", 35 | ]; 36 | 37 | /** 38 | * {@inheritdoc} 39 | * @see Network::$bip32ScriptTypeMap 40 | */ 41 | protected $bip32ScriptTypeMap = [ 42 | self::BIP32_PREFIX_XPUB => ScriptType::P2PKH, 43 | self::BIP32_PREFIX_XPRV => ScriptType::P2PKH, 44 | ]; 45 | 46 | /** 47 | * {@inheritdoc} 48 | * @see Network::$signedMessagePrefix 49 | */ 50 | protected $signedMessagePrefix = "Bitcoin Signed Message"; 51 | 52 | /** 53 | * {@inheritdoc} 54 | * @see Network::$p2pMagic 55 | */ 56 | protected $p2pMagic = "0709110b"; 57 | } 58 | -------------------------------------------------------------------------------- /src/Network/Networks/Dash.php: -------------------------------------------------------------------------------- 1 | "4c", 16 | self::BASE58_ADDRESS_P2SH => "10", 17 | self::BASE58_WIF => "cc", 18 | 19 | ]; 20 | 21 | /** 22 | * {@inheritdoc} 23 | * @see Network::$bip32PrefixMap 24 | */ 25 | protected $bip32PrefixMap = [ 26 | self::BIP32_PREFIX_XPUB => "0488b21e", 27 | self::BIP32_PREFIX_XPRV => "0488ade4", 28 | ]; 29 | 30 | /** 31 | * {@inheritdoc} 32 | * @see Network::$bip32ScriptTypeMap 33 | */ 34 | protected $bip32ScriptTypeMap = [ 35 | self::BIP32_PREFIX_XPUB => ScriptType::P2PKH, 36 | self::BIP32_PREFIX_XPRV => ScriptType::P2PKH, 37 | ]; 38 | 39 | /** 40 | * {@inheritdoc} 41 | * @see Network::$signedMessagePrefix 42 | */ 43 | protected $signedMessagePrefix = "DarkCoin Signed Message"; 44 | 45 | /** 46 | * {@inheritdoc} 47 | * @see Network::$p2pMagic 48 | */ 49 | protected $p2pMagic = "bd6b0cbf"; 50 | } 51 | -------------------------------------------------------------------------------- /src/Network/Networks/DashTestnet.php: -------------------------------------------------------------------------------- 1 | "8b", 16 | self::BASE58_ADDRESS_P2SH => "13", 17 | self::BASE58_WIF => "ef", 18 | ]; 19 | 20 | /** 21 | * {@inheritdoc} 22 | * @see Network::$bip32PrefixMap 23 | */ 24 | protected $bip32PrefixMap = [ 25 | self::BIP32_PREFIX_XPUB => "043587cf", 26 | self::BIP32_PREFIX_XPRV => "04358394", 27 | ]; 28 | 29 | /** 30 | * {@inheritdoc} 31 | * @see Network::$bip32ScriptTypeMap 32 | */ 33 | protected $bip32ScriptTypeMap = [ 34 | self::BIP32_PREFIX_XPUB => ScriptType::P2PKH, 35 | self::BIP32_PREFIX_XPRV => ScriptType::P2PKH, 36 | ]; 37 | 38 | /** 39 | * {@inheritdoc} 40 | * @see Network::$signedMessagePrefix 41 | */ 42 | protected $signedMessagePrefix = "DarkCoin Signed Message"; 43 | 44 | /** 45 | * {@inheritdoc} 46 | * @see Network::$p2pMagic 47 | */ 48 | protected $p2pMagic = "ffcae2ce"; 49 | } 50 | -------------------------------------------------------------------------------- /src/Network/Networks/Dogecoin.php: -------------------------------------------------------------------------------- 1 | "1e", 16 | self::BASE58_ADDRESS_P2SH => "16", 17 | self::BASE58_WIF => "9e", 18 | ]; 19 | 20 | /** 21 | * {@inheritdoc} 22 | * @see Network::$bip32PrefixMap 23 | */ 24 | protected $bip32PrefixMap = [ 25 | self::BIP32_PREFIX_XPUB => "02facafd", 26 | self::BIP32_PREFIX_XPRV => "02fac398", 27 | ]; 28 | 29 | /** 30 | * {@inheritdoc} 31 | * @see Network::$bip32ScriptTypeMap 32 | */ 33 | protected $bip32ScriptTypeMap = [ 34 | self::BIP32_PREFIX_XPUB => ScriptType::P2PKH, 35 | self::BIP32_PREFIX_XPRV => ScriptType::P2PKH, 36 | ]; 37 | 38 | 39 | /** 40 | * {@inheritdoc} 41 | * @see Network::$signedMessagePrefix 42 | */ 43 | protected $signedMessagePrefix = "Dogecoin Signed Message"; 44 | 45 | /** 46 | * {@inheritdoc} 47 | * @see Network::$p2pMagic 48 | */ 49 | protected $p2pMagic = "c0c0c0c0"; 50 | } 51 | -------------------------------------------------------------------------------- /src/Network/Networks/DogecoinTestnet.php: -------------------------------------------------------------------------------- 1 | "71", 16 | self::BASE58_ADDRESS_P2SH => "c4", 17 | self::BASE58_WIF => "f1", 18 | ]; 19 | 20 | /** 21 | * {@inheritdoc} 22 | * @see Network::$bip32PrefixMap 23 | */ 24 | protected $bip32PrefixMap = [ 25 | self::BIP32_PREFIX_XPUB => "043587cf", 26 | self::BIP32_PREFIX_XPRV => "04358394", 27 | ]; 28 | 29 | /** 30 | * {@inheritdoc} 31 | * @see Network::$bip32ScriptTypeMap 32 | */ 33 | protected $bip32ScriptTypeMap = [ 34 | self::BIP32_PREFIX_XPUB => ScriptType::P2PKH, 35 | self::BIP32_PREFIX_XPRV => ScriptType::P2PKH, 36 | ]; 37 | 38 | /** 39 | * {@inheritdoc} 40 | * @see Network::$signedMessagePrefix 41 | */ 42 | protected $signedMessagePrefix = "Dogecoin Signed Message"; 43 | 44 | /** 45 | * {@inheritdoc} 46 | * @see Network::$p2pMagic 47 | */ 48 | protected $p2pMagic = "dcb7c1fc"; 49 | } 50 | -------------------------------------------------------------------------------- /src/Network/Networks/Litecoin.php: -------------------------------------------------------------------------------- 1 | "30", 16 | self::BASE58_ADDRESS_P2SH => "32", 17 | self::BASE58_WIF => "b0", 18 | ]; 19 | 20 | /** 21 | * {@inheritdoc} 22 | * @see Network::$bech32PrefixMap 23 | */ 24 | protected $bech32PrefixMap = [ 25 | self::BECH32_PREFIX_SEGWIT => "ltc", 26 | ]; 27 | 28 | /** 29 | * {@inheritdoc} 30 | * @see Network::$bip32PrefixMap 31 | */ 32 | protected $bip32PrefixMap = [ 33 | self::BIP32_PREFIX_XPUB => "019da462", 34 | self::BIP32_PREFIX_XPRV => "019d9cfe", 35 | ]; 36 | 37 | /** 38 | * {@inheritdoc} 39 | * @see Network::$bip32ScriptTypeMap 40 | */ 41 | protected $bip32ScriptTypeMap = [ 42 | self::BIP32_PREFIX_XPUB => ScriptType::P2PKH, 43 | self::BIP32_PREFIX_XPRV => ScriptType::P2PKH, 44 | ]; 45 | 46 | /** 47 | * {@inheritdoc} 48 | * @see Network::$signedMessagePrefix 49 | */ 50 | protected $signedMessagePrefix = "Litecoin Signed Message"; 51 | 52 | /** 53 | * {@inheritdoc} 54 | * @see Network::$p2pMagic 55 | */ 56 | protected $p2pMagic = "dbb6c0fb"; 57 | } 58 | -------------------------------------------------------------------------------- /src/Network/Networks/LitecoinTestnet.php: -------------------------------------------------------------------------------- 1 | "6f", 16 | self::BASE58_ADDRESS_P2SH => "3a", 17 | self::BASE58_WIF => "ef", 18 | ]; 19 | 20 | /** 21 | * {@inheritdoc} 22 | * @see Network::$bip32PrefixMap 23 | */ 24 | protected $bip32PrefixMap = [ 25 | self::BIP32_PREFIX_XPUB => "043587cf", 26 | self::BIP32_PREFIX_XPRV => "04358394", 27 | ]; 28 | 29 | /** 30 | * {@inheritdoc} 31 | * @see Network::$bip32ScriptTypeMap 32 | */ 33 | protected $bip32ScriptTypeMap = [ 34 | self::BIP32_PREFIX_XPUB => ScriptType::P2PKH, 35 | self::BIP32_PREFIX_XPRV => ScriptType::P2PKH, 36 | ]; 37 | 38 | /** 39 | * {@inheritdoc} 40 | * @see Network::$bech32PrefixMap 41 | */ 42 | protected $bech32PrefixMap = [ 43 | self::BECH32_PREFIX_SEGWIT => "tltc", 44 | ]; 45 | 46 | /** 47 | * {@inheritdoc} 48 | * @see Network::$signedMessagePrefix 49 | */ 50 | protected $signedMessagePrefix = "Litecoin Signed Message"; 51 | 52 | /** 53 | * {@inheritdoc} 54 | * @see Network::$p2pMagic 55 | */ 56 | protected $p2pMagic = "f1c8d2fd"; 57 | } 58 | -------------------------------------------------------------------------------- /src/Network/Networks/Viacoin.php: -------------------------------------------------------------------------------- 1 | "47", 15 | self::BASE58_ADDRESS_P2SH => "21", 16 | self::BASE58_WIF => "c7", 17 | ]; 18 | 19 | /** 20 | * {@inheritdoc} 21 | * @see Network::$bech32PrefixMap 22 | */ 23 | protected $bech32PrefixMap = [ 24 | self::BECH32_PREFIX_SEGWIT => "via", 25 | ]; 26 | 27 | /** 28 | * @var array map of bip32 type to bytes 29 | */ 30 | protected $bip32PrefixMap = [ 31 | self::BIP32_PREFIX_XPUB => "0488b21e", 32 | self::BIP32_PREFIX_XPRV => "0488ade4", 33 | ]; 34 | 35 | /** 36 | * {@inheritdoc} 37 | * @see Network::$bip32ScriptTypeMap 38 | */ 39 | protected $bip32ScriptTypeMap = [ 40 | self::BIP32_PREFIX_XPUB => ScriptType::P2PKH, 41 | self::BIP32_PREFIX_XPRV => ScriptType::P2PKH, 42 | ]; 43 | 44 | /** 45 | * @var string - message prefix for bitcoin signed messages 46 | */ 47 | protected $signedMessagePrefix = "Viacoin Signed Message"; 48 | 49 | /** 50 | * @var string - 4 bytes for p2p magic 51 | */ 52 | protected $p2pMagic = "cbc6680f"; 53 | } 54 | -------------------------------------------------------------------------------- /src/Network/Networks/ViacoinTestnet.php: -------------------------------------------------------------------------------- 1 | "7f", 15 | self::BASE58_ADDRESS_P2SH => "c4", 16 | self::BASE58_WIF => "ff", 17 | ]; 18 | 19 | /** 20 | * @var array map of bip32 type to bytes 21 | */ 22 | protected $bip32PrefixMap = [ 23 | self::BIP32_PREFIX_XPUB => "043587cf", 24 | self::BIP32_PREFIX_XPRV => "04358394", 25 | ]; 26 | 27 | /** 28 | * {@inheritdoc} 29 | * @see Network::$bip32ScriptTypeMap 30 | */ 31 | protected $bip32ScriptTypeMap = [ 32 | self::BIP32_PREFIX_XPUB => ScriptType::P2PKH, 33 | self::BIP32_PREFIX_XPRV => ScriptType::P2PKH, 34 | ]; 35 | 36 | /** 37 | * {@inheritdoc} 38 | * @see Network::$bech32PrefixMap 39 | */ 40 | protected $bech32PrefixMap = [ 41 | self::BECH32_PREFIX_SEGWIT => "tvia", 42 | ]; 43 | 44 | /** 45 | * @var string - message prefix for bitcoin signed messages 46 | */ 47 | protected $signedMessagePrefix = "Viacoin Signed Message"; 48 | 49 | /** 50 | * @var string - 4 bytes for p2p magic 51 | */ 52 | protected $p2pMagic = "92efc5a9"; 53 | } 54 | -------------------------------------------------------------------------------- /src/Network/Networks/Zcash.php: -------------------------------------------------------------------------------- 1 | "1cb8", 17 | self::BASE58_ADDRESS_P2SH => "1cbd", 18 | self::BASE58_WIF => "80", 19 | ]; 20 | 21 | /** 22 | * {@inheritdoc} 23 | * @see Network::$bip32PrefixMap 24 | */ 25 | protected $bip32PrefixMap = [ 26 | // https://github.com/zcash/zcash/blob/master/src/chainparams.cpp#L146-L147 27 | self::BIP32_PREFIX_XPUB => "0488b21e", 28 | self::BIP32_PREFIX_XPRV => "0488ade4", 29 | ]; 30 | 31 | /** 32 | * {@inheritdoc} 33 | * @see Network::$bip32ScriptTypeMap 34 | */ 35 | protected $bip32ScriptTypeMap = [ 36 | self::BIP32_PREFIX_XPUB => ScriptType::P2PKH, 37 | self::BIP32_PREFIX_XPRV => ScriptType::P2PKH, 38 | ]; 39 | 40 | /** 41 | * {@inheritdoc} 42 | * @see Network::$signedMessagePrefix 43 | */ 44 | protected $signedMessagePrefix = "Zcash Signed Message"; 45 | 46 | /** 47 | * {@inheritdoc} 48 | * @see Network::$p2pMagic 49 | */ 50 | // https://github.com/zcash/zcash/blob/master/src/chainparams.cpp#L111-L114 51 | protected $p2pMagic = "6427e924"; 52 | } 53 | -------------------------------------------------------------------------------- /src/Network/Slip132/BitcoinRegistry.php: -------------------------------------------------------------------------------- 1 | type = $type; 36 | $this->script = $script; 37 | $this->solution = $solution; 38 | } 39 | 40 | /** 41 | * @return string 42 | */ 43 | public function getType(): string 44 | { 45 | return $this->type; 46 | } 47 | 48 | /** 49 | * @return ScriptInterface 50 | */ 51 | public function getScript(): ScriptInterface 52 | { 53 | return $this->script; 54 | } 55 | 56 | /** 57 | * @return mixed 58 | */ 59 | public function getSolution() 60 | { 61 | return $this->solution; 62 | } 63 | 64 | /** 65 | * @return bool 66 | */ 67 | public function canSign(): bool 68 | { 69 | return in_array($this->type, [ScriptType::MULTISIG, ScriptType::P2PK, ScriptType::P2PKH]); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Script/Consensus/BitcoinConsensus.php: -------------------------------------------------------------------------------- 1 | getBinary(), $amount, $tx->getBinary(), $nInputToSign, $flags, $error); 32 | } else { 33 | $verify = (bool) bitcoinconsensus_verify_script($scriptPubKey->getBinary(), $tx->getBaseSerialization()->getBinary(), $nInputToSign, $flags, $error); 34 | } 35 | 36 | return $verify; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Script/Consensus/ConsensusInterface.php: -------------------------------------------------------------------------------- 1 | adapter = $ecAdapter ?: Bitcoin::getEcAdapter(); 26 | } 27 | 28 | /** 29 | * @param TransactionInterface $tx 30 | * @param ScriptInterface $scriptPubKey 31 | * @param int $nInputToSign 32 | * @param int $flags 33 | * @param int $amount 34 | * @return bool 35 | */ 36 | public function verify(TransactionInterface $tx, ScriptInterface $scriptPubKey, int $flags, int $nInputToSign, int $amount): bool 37 | { 38 | $inputs = $tx->getInputs(); 39 | $interpreter = new Interpreter($this->adapter); 40 | return $interpreter->verify( 41 | $inputs[$nInputToSign]->getScript(), 42 | $scriptPubKey, 43 | $flags, 44 | new Checker($this->adapter, $tx, $nInputToSign, $amount), 45 | isset($tx->getWitnesses()[$nInputToSign]) ? $tx->getWitness($nInputToSign) : null 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Script/Interpreter/Checker.php: -------------------------------------------------------------------------------- 1 | getBuffer()->getBinary(); 30 | if (!isset($this->sigHashCache[$cacheCheck])) { 31 | if (SigHash::V1 === $sigVersion) { 32 | $hasher = new V1Hasher($this->transaction, $this->amount); 33 | } else { 34 | $hasher = new Hasher($this->transaction); 35 | } 36 | 37 | $hash = $hasher->calculate($script, $this->nInput, $sigHashType); 38 | $this->sigHashCache[$cacheCheck] = $hash->getBinary(); 39 | } else { 40 | $hash = new Buffer($this->sigHashCache[$cacheCheck], 32); 41 | } 42 | 43 | return $hash; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Script/P2shScript.php: -------------------------------------------------------------------------------- 1 | getOutputScript(); 38 | } else if ($script instanceof self) { 39 | throw new P2shScriptException("Cannot nest P2SH scripts."); 40 | } 41 | 42 | parent::__construct($script->getBuffer(), $opcodes); 43 | 44 | $this->scriptHash = $script->getScriptHash(); 45 | $this->outputScript = ScriptFactory::scriptPubKey()->p2sh($this->scriptHash); 46 | $this->address = new ScriptHashAddress($this->scriptHash); 47 | } 48 | 49 | /** 50 | * @throws P2shScriptException 51 | */ 52 | public function getWitnessScriptHash(): BufferInterface 53 | { 54 | throw new P2shScriptException("Cannot compute witness-script-hash for a P2shScript"); 55 | } 56 | 57 | /** 58 | * @return ScriptInterface 59 | */ 60 | public function getOutputScript(): ScriptInterface 61 | { 62 | return $this->outputScript; 63 | } 64 | 65 | /** 66 | * @return ScriptHashAddress 67 | */ 68 | public function getAddress(): ScriptHashAddress 69 | { 70 | return $this->address; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Script/Parser/Operation.php: -------------------------------------------------------------------------------- 1 | push = $opCode >= 0 && $opCode <= Opcodes::OP_PUSHDATA4; 48 | $this->opCode = $opCode; 49 | $this->pushData = $pushData; 50 | $this->pushDataSize = $pushDataSize; 51 | } 52 | 53 | /** 54 | * @return BufferInterface|int 55 | */ 56 | public function encode() 57 | { 58 | if ($this->push) { 59 | return $this->pushData; 60 | } else { 61 | return $this->opCode; 62 | } 63 | } 64 | 65 | /** 66 | * @return bool 67 | */ 68 | public function isPush(): bool 69 | { 70 | return $this->push; 71 | } 72 | 73 | /** 74 | * @return bool 75 | */ 76 | public function isLogical(): bool 77 | { 78 | return !$this->isPush() && in_array($this->opCode, self::$logical); 79 | } 80 | 81 | 82 | /** 83 | * @return int 84 | */ 85 | public function getOp(): int 86 | { 87 | return $this->opCode; 88 | } 89 | 90 | /** 91 | * @return BufferInterface 92 | */ 93 | public function getData(): BufferInterface 94 | { 95 | return $this->pushData; 96 | } 97 | 98 | /** 99 | * @return int 100 | */ 101 | public function getDataSize(): int 102 | { 103 | if (!$this->push) { 104 | throw new \RuntimeException("Op wasn't a push operation"); 105 | } 106 | 107 | return $this->pushDataSize; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Script/Path/ParsedScript.php: -------------------------------------------------------------------------------- 1 | isRoot()) { 35 | throw new \RuntimeException("LogicOpNode was not for root"); 36 | } 37 | 38 | $descriptorIdx = 0; 39 | $keyedIdxMap = []; // descriptor => ScriptBranch 40 | foreach ($branches as $branch) { 41 | $descriptor = $branch->getPath(); 42 | $descriptorKey = json_encode($descriptor); 43 | if (array_key_exists($descriptorKey, $keyedIdxMap)) { 44 | throw new \RuntimeException("Duplicate logical pathway, invalid ScriptBranch found"); 45 | } 46 | 47 | $keyedIdxMap[$descriptorKey] = $branch; 48 | $descriptorIdx++; 49 | } 50 | 51 | $this->descriptorMap = $keyedIdxMap; 52 | $this->script = $script; 53 | $this->ast = $ast; 54 | } 55 | 56 | /** 57 | * @return bool 58 | */ 59 | public function hasMultipleBranches() 60 | { 61 | return $this->ast->hasChildren(); 62 | } 63 | 64 | /** 65 | * Returns a list of paths for this script. This is not 66 | * always guaranteed to be in order, so take care that 67 | * you actually work out the paths in advance of signing, 68 | * and hard code them somehow. 69 | * 70 | * @return array[] - array of paths 71 | */ 72 | public function getPaths() 73 | { 74 | return array_map(function (ScriptBranch $branch) { 75 | return $branch->getPath(); 76 | }, $this->descriptorMap); 77 | } 78 | 79 | /** 80 | * Look up the branch by it's path 81 | * 82 | * @param array $branchDesc 83 | * @return bool|ScriptBranch 84 | */ 85 | public function getBranchByPath(array $branchDesc) 86 | { 87 | $key = json_encode($branchDesc); 88 | if (!array_key_exists($key, $this->descriptorMap)) { 89 | throw new \RuntimeException("Unknown logical pathway"); 90 | } 91 | 92 | return $this->descriptorMap[$key]; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Script/Path/PathTracer.php: -------------------------------------------------------------------------------- 1 | segments[] = $this->current; 38 | $this->current = []; 39 | } 40 | 41 | /** 42 | * Add an operation to current segment 43 | * @param Operation $operation 44 | */ 45 | private function addToCurrent(Operation $operation) 46 | { 47 | $this->current[] = $operation; 48 | } 49 | 50 | /** 51 | * @param Operation $operation 52 | */ 53 | public function operation(Operation $operation) 54 | { 55 | if ($this->done) { 56 | throw new \RuntimeException("Cannot add operation to finished PathTracer"); 57 | } 58 | 59 | if ($operation->isLogical()) { 60 | // Logical opcodes mean the end of a segment 61 | if (count($this->current) > 0) { 62 | $this->makeSegment(); 63 | } 64 | 65 | $this->addToCurrent($operation); 66 | $this->makeSegment(); 67 | } else { 68 | $this->addToCurrent($operation); 69 | } 70 | } 71 | 72 | /** 73 | * @return array 74 | */ 75 | public function done(): array 76 | { 77 | if ($this->done) { 78 | return $this->segments; 79 | } 80 | 81 | if (count($this->current) > 0) { 82 | $this->makeSegment(); 83 | } 84 | 85 | $this->done = true; 86 | 87 | return $this->segments; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Script/Path/ScriptBranch.php: -------------------------------------------------------------------------------- 1 | fullScript = $fullScript; 35 | $this->branch = $logicalPath; 36 | $this->scriptSections = $scriptSections; 37 | } 38 | 39 | /** 40 | * @return bool[] 41 | */ 42 | public function getPath(): array 43 | { 44 | return $this->branch; 45 | } 46 | 47 | /** 48 | * @return array[] 49 | */ 50 | public function getScriptSections(): array 51 | { 52 | return $this->scriptSections; 53 | } 54 | 55 | /** 56 | * @return array 57 | */ 58 | public function getOps(): array 59 | { 60 | $sequence = []; 61 | foreach ($this->getScriptSections() as $segment) { 62 | $sequence = array_merge($sequence, $segment); 63 | } 64 | return $sequence; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Script/ScriptInterface.php: -------------------------------------------------------------------------------- 1 | offsetGet($i)->equals($witness->offsetGet($i))) { 26 | return false; 27 | } 28 | } 29 | 30 | return true; 31 | } 32 | 33 | /** 34 | * @return \BitWasp\Buffertools\BufferInterface 35 | */ 36 | public function getBuffer(): BufferInterface 37 | { 38 | return (new ScriptWitnessSerializer())->serialize($this); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Script/ScriptWitnessInterface.php: -------------------------------------------------------------------------------- 1 | version < 0 || $this->version > 16) { 36 | throw new \RuntimeException("Invalid witness program version"); 37 | } 38 | 39 | if ($this->version === 0 && ($program->getSize() !== 20 && $program->getSize() !== 32)) { 40 | throw new \RuntimeException('Invalid size for V0 witness program - must be 20 or 32 bytes'); 41 | } 42 | 43 | $this->version = $version; 44 | $this->program = $program; 45 | } 46 | 47 | /** 48 | * @param BufferInterface $program 49 | * @return WitnessProgram 50 | */ 51 | public static function v0(BufferInterface $program): WitnessProgram 52 | { 53 | if ($program->getSize() === 20) { 54 | return new self(self::V0, $program); 55 | } else if ($program->getSize() === 32) { 56 | return new self(self::V0, $program); 57 | } else { 58 | throw new \RuntimeException('Invalid size for V0 witness program - must be 20 or 32 bytes'); 59 | } 60 | } 61 | 62 | /** 63 | * @return int 64 | */ 65 | public function getVersion(): int 66 | { 67 | return $this->version; 68 | } 69 | 70 | /** 71 | * @return BufferInterface 72 | */ 73 | public function getProgram(): BufferInterface 74 | { 75 | return $this->program; 76 | } 77 | 78 | /** 79 | * @return ScriptInterface 80 | */ 81 | public function getScript(): ScriptInterface 82 | { 83 | if (null === $this->outputScript) { 84 | $this->outputScript = ScriptFactory::sequence([encodeOpN($this->version), $this->program]); 85 | } 86 | 87 | return $this->outputScript; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Script/WitnessScript.php: -------------------------------------------------------------------------------- 1 | getBuffer(), $opcodes); 48 | 49 | $this->witnessScriptHash = $script->getWitnessScriptHash(); 50 | $this->outputScript = ScriptFactory::scriptPubKey()->p2wsh($this->witnessScriptHash); 51 | } 52 | 53 | /** 54 | * @return WitnessProgram 55 | */ 56 | public function getWitnessProgram(): WitnessProgram 57 | { 58 | if (null === $this->witnessProgram) { 59 | $this->witnessProgram = WitnessProgram::v0($this->witnessScriptHash); 60 | } 61 | 62 | return $this->witnessProgram; 63 | } 64 | 65 | /** 66 | * @return SegwitAddress 67 | */ 68 | public function getAddress(): SegwitAddress 69 | { 70 | if (null === $this->address) { 71 | $this->address = new SegwitAddress($this->getWitnessProgram()); 72 | } 73 | 74 | return $this->address; 75 | } 76 | 77 | /** 78 | * @return ScriptInterface 79 | */ 80 | public function getOutputScript(): ScriptInterface 81 | { 82 | return $this->getWitnessProgram()->getScript(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Script/functions.php: -------------------------------------------------------------------------------- 1 | = Opcodes::OP_1 && $op <= Opcodes::OP_16)) { 14 | throw new \RuntimeException("Invalid opcode"); 15 | } 16 | 17 | return $op - (Opcodes::OP_1 - 1); 18 | } 19 | 20 | function encodeOpN(int $op): int 21 | { 22 | if ($op === 0) { 23 | return Opcodes::OP_0; 24 | } 25 | 26 | if (!($op === -1 || $op >= 1 && $op <= 16)) { 27 | throw new \RuntimeException("Invalid value"); 28 | } 29 | 30 | return Opcodes::OP_1 + $op - 1; 31 | } 32 | -------------------------------------------------------------------------------- /src/Serializable.php: -------------------------------------------------------------------------------- 1 | getBuffer()->getHex(); 15 | } 16 | 17 | /** 18 | * @return string 19 | */ 20 | public function getBinary(): string 21 | { 22 | return $this->getBuffer()->getBinary(); 23 | } 24 | 25 | /** 26 | * @return int 27 | */ 28 | public function getInt() 29 | { 30 | return $this->getBuffer()->getInt(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/SerializableInterface.php: -------------------------------------------------------------------------------- 1 | blockSerializer = $blockSerializer; 44 | $this->magic = Types::bytestringle(4); 45 | $this->size = Types::uint32le(); 46 | $this->network = $network; 47 | } 48 | 49 | /** 50 | * @param BlockInterface $block 51 | * @return BufferInterface 52 | */ 53 | public function serialize(BlockInterface $block): BufferInterface 54 | { 55 | $buffer = $this->blockSerializer->serialize($block); 56 | return new Buffer(sprintf( 57 | "%s%s%s", 58 | strrev(pack("H*", $this->network->getNetMagicBytes())), 59 | pack("V", $buffer->getSize()), 60 | $buffer->getBinary() 61 | )); 62 | } 63 | 64 | /** 65 | * @param Parser $parser 66 | * @return BlockInterface 67 | * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange 68 | * @throws \Exception 69 | */ 70 | public function fromParser(Parser $parser) 71 | { 72 | /** 73 | * @var Buffer $bytes 74 | * @var int $blockSize 75 | */ 76 | list ($bytes, $blockSize) = [$this->magic->read($parser), (int) $this->size->read($parser)]; 77 | if ($bytes->getHex() !== $this->network->getNetMagicBytes()) { 78 | throw new \RuntimeException('Block version bytes did not match network'); 79 | } 80 | 81 | return $this->blockSerializer->fromParser(new Parser($parser->readBytes($blockSize))); 82 | } 83 | 84 | /** 85 | * @param BufferInterface $data 86 | * @return BlockInterface 87 | */ 88 | public function parse(BufferInterface $data): BlockInterface 89 | { 90 | return $this->fromParser(new Parser($data)); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Serializer/Block/BlockHeaderSerializer.php: -------------------------------------------------------------------------------- 1 | hash = Types::bytestringle(32); 35 | $this->uint32le = Types::uint32le(); 36 | $this->int32le = Types::int32le(); 37 | } 38 | 39 | /** 40 | * @param BufferInterface $buffer 41 | * @return BlockHeaderInterface 42 | * @throws ParserOutOfRange 43 | */ 44 | public function parse(BufferInterface $buffer): BlockHeaderInterface 45 | { 46 | return $this->fromParser(new Parser($buffer)); 47 | } 48 | 49 | /** 50 | * @param Parser $parser 51 | * @return BlockHeaderInterface 52 | * @throws ParserOutOfRange 53 | */ 54 | public function fromParser(Parser $parser): BlockHeaderInterface 55 | { 56 | try { 57 | return new BlockHeader( 58 | (int) $this->int32le->read($parser), 59 | $this->hash->read($parser), 60 | $this->hash->read($parser), 61 | (int) $this->uint32le->read($parser), 62 | (int) $this->uint32le->read($parser), 63 | (int) $this->uint32le->read($parser) 64 | ); 65 | } catch (ParserOutOfRange $e) { 66 | throw new ParserOutOfRange('Failed to extract full block header from parser'); 67 | } 68 | } 69 | 70 | /** 71 | * @param BlockHeaderInterface $header 72 | * @return BufferInterface 73 | */ 74 | public function serialize(BlockHeaderInterface $header): BufferInterface 75 | { 76 | return new Buffer( 77 | $this->int32le->write($header->getVersion()) . 78 | $this->hash->write($header->getPrevBlock()) . 79 | $this->hash->write($header->getMerkleRoot()) . 80 | $this->uint32le->write($header->getTimestamp()) . 81 | $this->uint32le->write($header->getBits()) . 82 | $this->uint32le->write($header->getNonce()) 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Serializer/Block/BlockSerializerInterface.php: -------------------------------------------------------------------------------- 1 | headerSerializer = $header; 32 | $this->treeSerializer = $tree; 33 | } 34 | 35 | /** 36 | * @param Parser $parser 37 | * @return FilteredBlock 38 | */ 39 | public function fromParser(Parser $parser): FilteredBlock 40 | { 41 | return new FilteredBlock( 42 | $this->headerSerializer->fromParser($parser), 43 | $this->treeSerializer->fromParser($parser) 44 | ); 45 | } 46 | 47 | /** 48 | * @param BufferInterface $data 49 | * @return FilteredBlock 50 | */ 51 | public function parse(BufferInterface $data): FilteredBlock 52 | { 53 | return $this->fromParser(new Parser($data)); 54 | } 55 | 56 | /** 57 | * @param FilteredBlock $merkleBlock 58 | * @return BufferInterface 59 | */ 60 | public function serialize(FilteredBlock $merkleBlock): BufferInterface 61 | { 62 | return Buffertools::concat( 63 | $this->headerSerializer->serialize($merkleBlock->getHeader()), 64 | $this->treeSerializer->serialize($merkleBlock->getPartialTree()) 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Serializer/Bloom/BloomFilterSerializer.php: -------------------------------------------------------------------------------- 1 | uint32le = Types::uint32le(); 33 | $this->uint8le = Types::uint8le(); 34 | $this->varint = Types::varint(); 35 | } 36 | 37 | /** 38 | * @param BloomFilter $filter 39 | * @return BufferInterface 40 | */ 41 | public function serialize(BloomFilter $filter): BufferInterface 42 | { 43 | $parser = new Parser(); 44 | $parser->appendBinary($this->varint->write(count($filter->getData()))); 45 | foreach ($filter->getData() as $i) { 46 | $parser->appendBinary(pack('c', $i)); 47 | } 48 | 49 | $parser->appendBinary($this->uint32le->write($filter->getNumHashFuncs())); 50 | $parser->appendBinary($this->uint32le->write($filter->getTweak())); 51 | $parser->appendBinary($this->uint8le->write($filter->getFlags())); 52 | 53 | return $parser->getBuffer(); 54 | } 55 | 56 | /** 57 | * @param Parser $parser 58 | * @return BloomFilter 59 | */ 60 | public function fromParser(Parser $parser): BloomFilter 61 | { 62 | $varint = (int) $this->varint->read($parser); 63 | $vData = []; 64 | for ($i = 0; $i < $varint; $i++) { 65 | $vData[] = (int) $this->uint8le->read($parser); 66 | } 67 | 68 | $nHashFuncs = (int) $this->uint32le->read($parser); 69 | $nTweak = (int) $this->uint32le->read($parser); 70 | $flags = (int) $this->uint8le->read($parser); 71 | 72 | return new BloomFilter( 73 | Bitcoin::getMath(), 74 | $vData, 75 | $nHashFuncs, 76 | $nTweak, 77 | $flags 78 | ); 79 | } 80 | 81 | /** 82 | * @param BufferInterface $data 83 | * @return BloomFilter 84 | */ 85 | public function parse(BufferInterface $data): BloomFilter 86 | { 87 | return $this->fromParser(new Parser($data)); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Serializer/Chain/BlockLocatorSerializer.php: -------------------------------------------------------------------------------- 1 | varint = Types::varint(); 28 | $this->bytestring32le = Types::bytestringle(32); 29 | } 30 | 31 | /** 32 | * @param Parser $parser 33 | * @return BlockLocator 34 | */ 35 | public function fromParser(Parser $parser): BlockLocator 36 | { 37 | $numHashes = $this->varint->read($parser); 38 | $hashes = []; 39 | for ($i = 0; $i < $numHashes; $i++) { 40 | $hashes[] = $this->bytestring32le->read($parser); 41 | } 42 | 43 | $hashStop = $this->bytestring32le->read($parser); 44 | 45 | return new BlockLocator($hashes, $hashStop); 46 | } 47 | 48 | /** 49 | * @param BufferInterface $data 50 | * @return BlockLocator 51 | */ 52 | public function parse(BufferInterface $data): BlockLocator 53 | { 54 | return $this->fromParser(new Parser($data)); 55 | } 56 | 57 | /** 58 | * @param BlockLocator $blockLocator 59 | * @return BufferInterface 60 | * @throws \Exception 61 | */ 62 | public function serialize(BlockLocator $blockLocator): BufferInterface 63 | { 64 | $binary = $this->varint->write(count($blockLocator->getHashes())); 65 | foreach ($blockLocator->getHashes() as $hash) { 66 | $binary .= $this->bytestring32le->write($hash); 67 | } 68 | 69 | $binary .= $this->bytestring32le->write($blockLocator->getHashStop()); 70 | return new Buffer($binary); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Serializer/Key/HierarchicalKey/Base58ExtendedKeySerializer.php: -------------------------------------------------------------------------------- 1 | serializer = $hdSerializer; 24 | } 25 | 26 | /** 27 | * @param NetworkInterface $network 28 | * @param HierarchicalKey $key 29 | * @return string 30 | * @throws \Exception 31 | */ 32 | public function serialize(NetworkInterface $network, HierarchicalKey $key): string 33 | { 34 | return Base58::encodeCheck($this->serializer->serialize($network, $key)); 35 | } 36 | 37 | /** 38 | * @param NetworkInterface $network 39 | * @param string $base58 40 | * @return HierarchicalKey 41 | * @throws \BitWasp\Bitcoin\Exceptions\Base58ChecksumFailure 42 | * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange 43 | */ 44 | public function parse(NetworkInterface $network, string $base58): HierarchicalKey 45 | { 46 | return $this->serializer->parse($network, Base58::decodeCheck($base58)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Serializer/Key/HierarchicalKey/RawKeyParams.php: -------------------------------------------------------------------------------- 1 | prefix = $prefix; 53 | $this->depth = $depth; 54 | $this->parentFpr = $parentFingerprint; 55 | $this->sequence = $sequence; 56 | $this->chainCode = $chainCode; 57 | $this->keyData = $keyData; 58 | } 59 | 60 | /** 61 | * @return string 62 | */ 63 | public function getPrefix(): string 64 | { 65 | return $this->prefix; 66 | } 67 | 68 | /** 69 | * @return int 70 | */ 71 | public function getDepth(): int 72 | { 73 | return $this->depth; 74 | } 75 | 76 | /** 77 | * @return int 78 | */ 79 | public function getParentFingerprint(): int 80 | { 81 | return $this->parentFpr; 82 | } 83 | 84 | /** 85 | * @return int 86 | */ 87 | public function getSequence(): int 88 | { 89 | return $this->sequence; 90 | } 91 | 92 | /** 93 | * @return BufferInterface 94 | */ 95 | public function getChainCode(): BufferInterface 96 | { 97 | return $this->chainCode; 98 | } 99 | 100 | /** 101 | * @return BufferInterface 102 | */ 103 | public function getKeyData(): BufferInterface 104 | { 105 | return $this->keyData; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Serializer/Script/ScriptWitnessSerializer.php: -------------------------------------------------------------------------------- 1 | varstring = Types::varstring(); 29 | $this->varint = Types::varint(); 30 | } 31 | 32 | /** 33 | * @param Parser $parser 34 | * @return ScriptWitnessInterface 35 | */ 36 | public function fromParser(Parser $parser): ScriptWitnessInterface 37 | { 38 | $size = $this->varint->read($parser); 39 | $entries = []; 40 | for ($j = 0; $j < $size; $j++) { 41 | $entries[] = $this->varstring->read($parser); 42 | } 43 | 44 | return new ScriptWitness(...$entries); 45 | } 46 | 47 | /** 48 | * @param ScriptWitnessInterface $witness 49 | * @return BufferInterface 50 | */ 51 | public function serialize(ScriptWitnessInterface $witness): BufferInterface 52 | { 53 | $binary = $this->varint->write($witness->count()); 54 | foreach ($witness as $value) { 55 | $binary .= $this->varstring->write($value); 56 | } 57 | 58 | return new Buffer($binary); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Serializer/Signature/TransactionSignatureSerializer.php: -------------------------------------------------------------------------------- 1 | sigSerializer = $sigSerializer; 26 | } 27 | 28 | /** 29 | * @param TransactionSignatureInterface $txSig 30 | * @return BufferInterface 31 | */ 32 | public function serialize(TransactionSignatureInterface $txSig): BufferInterface 33 | { 34 | return new Buffer($this->sigSerializer->serialize($txSig->getSignature())->getBinary() . pack('C', $txSig->getHashType())); 35 | } 36 | 37 | /** 38 | * @param BufferInterface $buffer 39 | * @return TransactionSignatureInterface 40 | * @throws \Exception 41 | */ 42 | public function parse(BufferInterface $buffer): TransactionSignatureInterface 43 | { 44 | $adapter = $this->sigSerializer->getEcAdapter(); 45 | 46 | if ($buffer->getSize() < 1) { 47 | throw new \RuntimeException("Empty signature"); 48 | } 49 | 50 | return new TransactionSignature( 51 | $adapter, 52 | $this->sigSerializer->parse($buffer->slice(0, -1)), 53 | (int) $buffer->slice(-1)->getInt() 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Serializer/Transaction/OutPointSerializer.php: -------------------------------------------------------------------------------- 1 | txid = Types::bytestringle(32); 29 | $this->vout = Types::uint32le(); 30 | } 31 | 32 | /** 33 | * @param OutPointInterface $outpoint 34 | * @return BufferInterface 35 | * @throws \Exception 36 | */ 37 | public function serialize(OutPointInterface $outpoint): BufferInterface 38 | { 39 | return new Buffer( 40 | $this->txid->write($outpoint->getTxId()) . 41 | $this->vout->write($outpoint->getVout()) 42 | ); 43 | } 44 | 45 | /** 46 | * @param Parser $parser 47 | * @return OutPointInterface 48 | * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange 49 | */ 50 | public function fromParser(Parser $parser): OutPointInterface 51 | { 52 | return new OutPoint( 53 | new Buffer(strrev($parser->readBytes(32)->getBinary()), 32), 54 | unpack("V", $parser->readBytes(4)->getBinary())[1] 55 | ); 56 | } 57 | 58 | /** 59 | * @param BufferInterface $data 60 | * @return OutPointInterface 61 | * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange 62 | */ 63 | public function parse(BufferInterface $data): OutPointInterface 64 | { 65 | return $this->fromParser(new Parser($data)); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Serializer/Transaction/OutPointSerializerInterface.php: -------------------------------------------------------------------------------- 1 | uint64le = Types::uint64le(); 40 | $this->varstring = Types::varstring(); 41 | $this->opcodes = $opcodes ?: new Opcodes(); 42 | } 43 | 44 | /** 45 | * @param TransactionOutputInterface $output 46 | * @return BufferInterface 47 | */ 48 | public function serialize(TransactionOutputInterface $output): BufferInterface 49 | { 50 | return new Buffer( 51 | $this->uint64le->write($output->getValue()) . 52 | $this->varstring->write($output->getScript()->getBuffer()) 53 | ); 54 | } 55 | 56 | /** 57 | * @param Parser $parser 58 | * @return TransactionOutputInterface 59 | * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange 60 | */ 61 | public function fromParser(Parser $parser): TransactionOutputInterface 62 | { 63 | return new TransactionOutput( 64 | (int) $this->uint64le->read($parser), 65 | new Script($this->varstring->read($parser), $this->opcodes) 66 | ); 67 | } 68 | 69 | /** 70 | * @param BufferInterface $string 71 | * @return TransactionOutputInterface 72 | * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange 73 | */ 74 | public function parse(BufferInterface $string): TransactionOutputInterface 75 | { 76 | return $this->fromParser(new Parser($string)); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Serializer/Transaction/TransactionSerializerInterface.php: -------------------------------------------------------------------------------- 1 | parse($buffer); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Signature/SignatureSort.php: -------------------------------------------------------------------------------- 1 | ecAdapter = $ecAdapter ?: Bitcoin::getEcAdapter(); 25 | } 26 | 27 | /** 28 | * @param \BitWasp\Bitcoin\Crypto\EcAdapter\Signature\SignatureInterface[] $signatures 29 | * @param \BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface[] $publicKeys 30 | * @param BufferInterface $messageHash 31 | * @return \SplObjectStorage 32 | */ 33 | public function link(array $signatures, array $publicKeys, BufferInterface $messageHash): \SplObjectStorage 34 | { 35 | $sigCount = count($signatures); 36 | $storage = new \SplObjectStorage(); 37 | foreach ($signatures as $signature) { 38 | foreach ($publicKeys as $key) { 39 | if ($key->verify($messageHash, $signature)) { 40 | $storage->attach($key, $signature); 41 | if (count($storage) === $sigCount) { 42 | break 2; 43 | } 44 | 45 | break; 46 | } 47 | } 48 | } 49 | 50 | return $storage; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Signature/SignatureSortInterface.php: -------------------------------------------------------------------------------- 1 | parse($buffer); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Signature/TransactionSignatureInterface.php: -------------------------------------------------------------------------------- 1 | ecAdapter, $tx, $nInput, $txOut->getValue(), $this->txSigSerializer, $this->pubKeySerializer); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Transaction/Factory/Checker/CheckerCreatorBase.php: -------------------------------------------------------------------------------- 1 | ecAdapter = $ecAdapter; 43 | $this->txSigSerializer = $txSigSerializer; 44 | $this->pubKeySerializer = $pubKeySerializer; 45 | } 46 | 47 | /** 48 | * @param TransactionInterface $tx 49 | * @param int $nInput 50 | * @param TransactionOutputInterface $txOut 51 | * @return CheckerBase 52 | */ 53 | abstract public function create(TransactionInterface $tx, int $nInput, TransactionOutputInterface $txOut): CheckerBase; 54 | } 55 | -------------------------------------------------------------------------------- /src/Transaction/Factory/Conditional.php: -------------------------------------------------------------------------------- 1 | opcode = $opcode; 39 | } 40 | 41 | /** 42 | * @return int 43 | */ 44 | public function getOp(): int 45 | { 46 | return $this->opcode; 47 | } 48 | 49 | /** 50 | * @param bool $value 51 | */ 52 | public function setValue(bool $value) 53 | { 54 | $this->value = $value; 55 | } 56 | 57 | /** 58 | * @return bool 59 | */ 60 | public function hasValue(): bool 61 | { 62 | return null !== $this->value; 63 | } 64 | 65 | /** 66 | * @return bool 67 | */ 68 | public function getValue(): bool 69 | { 70 | if (null === $this->value) { 71 | throw new \RuntimeException("Value not set on conditional"); 72 | } 73 | 74 | return $this->value; 75 | } 76 | 77 | /** 78 | * @param Checksig $checksig 79 | */ 80 | public function providedBy(Checksig $checksig) 81 | { 82 | $this->providedBy = $checksig; 83 | } 84 | 85 | /** 86 | * @return BufferInterface[] 87 | */ 88 | public function serialize(): array 89 | { 90 | if ($this->hasValue() && null === $this->providedBy) { 91 | return [$this->value ? new Buffer("\x01") : new Buffer()]; 92 | } 93 | 94 | return []; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Transaction/Factory/ScriptInfo/CheckLocktimeVerify.php: -------------------------------------------------------------------------------- 1 | Locktime::INT_MAX) { 36 | throw new \RuntimeException("nLockTime exceeds maximum value"); 37 | } 38 | 39 | $this->nLockTime = $nLockTime; 40 | $this->toBlock = (new Locktime())->isLockedToBlock($nLockTime); 41 | } 42 | 43 | /** 44 | * @param Operation[] $chunks 45 | * @param bool $fMinimal 46 | * @return CheckLocktimeVerify 47 | */ 48 | public static function fromDecodedScript(array $chunks, bool $fMinimal = false): CheckLocktimeVerify 49 | { 50 | if (count($chunks) !== 3) { 51 | throw new \RuntimeException("Invalid number of items for CLTV"); 52 | } 53 | 54 | if (!$chunks[0]->isPush()) { 55 | throw new \InvalidArgumentException('CLTV script had invalid value for time'); 56 | } 57 | 58 | if ($chunks[1]->getOp() !== Opcodes::OP_CHECKLOCKTIMEVERIFY) { 59 | throw new \InvalidArgumentException('CLTV script invalid opcode'); 60 | } 61 | 62 | if ($chunks[2]->getOp() !== Opcodes::OP_DROP) { 63 | throw new \InvalidArgumentException('CLTV script invalid opcode'); 64 | } 65 | 66 | $numLockTime = Number::buffer($chunks[0]->getData(), $fMinimal, 5); 67 | 68 | return new CheckLocktimeVerify($numLockTime->getInt()); 69 | } 70 | 71 | /** 72 | * @param ScriptInterface $script 73 | * @return CheckLocktimeVerify 74 | */ 75 | public static function fromScript(ScriptInterface $script): self 76 | { 77 | return static::fromDecodedScript($script->getScriptParser()->decode()); 78 | } 79 | 80 | /** 81 | * @return int 82 | */ 83 | public function getLocktime(): int 84 | { 85 | return $this->nLockTime; 86 | } 87 | 88 | /** 89 | * @return bool 90 | */ 91 | public function isLockedToBlock(): bool 92 | { 93 | return $this->toBlock; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Transaction/Factory/SigValues.php: -------------------------------------------------------------------------------- 1 | scriptSig = $scriptSig; 30 | $this->scriptWitness = $scriptWitness; 31 | } 32 | 33 | /** 34 | * @return ScriptInterface 35 | */ 36 | public function getScriptSig(): ScriptInterface 37 | { 38 | return $this->scriptSig; 39 | } 40 | 41 | /** 42 | * @return ScriptWitnessInterface 43 | */ 44 | public function getScriptWitness(): ScriptWitnessInterface 45 | { 46 | return $this->scriptWitness; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Transaction/Factory/TimeLock.php: -------------------------------------------------------------------------------- 1 | info = $info; 33 | } 34 | 35 | /** 36 | * @return CheckLocktimeVerify|CheckSequenceVerify 37 | */ 38 | public function getInfo() 39 | { 40 | return $this->info; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Transaction/Mutator/AbstractCollectionMutator.php: -------------------------------------------------------------------------------- 1 | set->toArray(); 20 | } 21 | 22 | /** 23 | * @return bool 24 | */ 25 | public function isNull(): bool 26 | { 27 | return count($this->set) === 0; 28 | } 29 | 30 | /** 31 | * @return int 32 | */ 33 | public function count(): int 34 | { 35 | return $this->set->count(); 36 | } 37 | 38 | /** 39 | * 40 | */ 41 | public function rewind() 42 | { 43 | $this->set->rewind(); 44 | } 45 | 46 | /** 47 | * @return mixed 48 | */ 49 | public function current() 50 | { 51 | return $this->set->current(); 52 | } 53 | 54 | /** 55 | * @return int 56 | */ 57 | public function key() 58 | { 59 | return $this->set->key(); 60 | } 61 | 62 | /** 63 | * 64 | */ 65 | public function next() 66 | { 67 | $this->set->next(); 68 | } 69 | 70 | /** 71 | * @return bool 72 | */ 73 | public function valid() 74 | { 75 | return $this->set->valid(); 76 | } 77 | 78 | /** 79 | * @param int $offset 80 | * @return bool 81 | */ 82 | public function offsetExists($offset) 83 | { 84 | return $this->set->offsetExists($offset); 85 | } 86 | 87 | /** 88 | * @param int $offset 89 | */ 90 | public function offsetUnset($offset) 91 | { 92 | if (!$this->offsetExists($offset)) { 93 | throw new \InvalidArgumentException('Offset does not exist'); 94 | } 95 | 96 | $this->set->offsetUnset($offset); 97 | } 98 | 99 | /** 100 | * @param int $offset 101 | * @return mixed 102 | */ 103 | public function offsetGet($offset) 104 | { 105 | if (!$this->set->offsetExists($offset)) { 106 | throw new \OutOfRangeException('Nothing found at this offset'); 107 | } 108 | return $this->set->offsetGet($offset); 109 | } 110 | 111 | /** 112 | * @param int $offset 113 | * @param mixed $value 114 | */ 115 | public function offsetSet($offset, $value) 116 | { 117 | $this->set->offsetSet($offset, $value); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/Transaction/Mutator/OutputMutator.php: -------------------------------------------------------------------------------- 1 | output = $output; 24 | } 25 | 26 | /** 27 | * @return TransactionOutputInterface 28 | */ 29 | public function done(): TransactionOutputInterface 30 | { 31 | return $this->output; 32 | } 33 | 34 | /** 35 | * @param array $array 36 | * @return $this 37 | */ 38 | private function replace(array $array) 39 | { 40 | $this->output = new TransactionOutput( 41 | array_key_exists('value', $array) ? $array['value'] : $this->output->getValue(), 42 | array_key_exists('script', $array) ? $array['script'] : $this->output->getScript() 43 | ); 44 | 45 | return $this; 46 | } 47 | 48 | /** 49 | * @param int $value 50 | * @return $this 51 | */ 52 | public function value(int $value) 53 | { 54 | return $this->replace(array('value' => $value)); 55 | } 56 | 57 | /** 58 | * @param ScriptInterface $script 59 | * @return $this 60 | */ 61 | public function script(ScriptInterface $script) 62 | { 63 | return $this->replace(array('script' => $script)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Transaction/OutPoint.php: -------------------------------------------------------------------------------- 1 | getSize() !== 32) { 35 | throw new InvalidHashLengthException('OutPoint: hashPrevOut must be a 32-byte Buffer'); 36 | } 37 | 38 | if ($nPrevOutput < 0 || $nPrevOutput > IntRange::U32_MAX) { 39 | throw new \InvalidArgumentException('nPrevOut must be between 0 and 0xffffffff'); 40 | } 41 | 42 | $this->hashPrevOutput = $hashPrevOutput; 43 | $this->nPrevOutput = $nPrevOutput; 44 | } 45 | 46 | /** 47 | * @return OutPointInterface 48 | */ 49 | public static function makeCoinbase(): OutPointInterface 50 | { 51 | return new OutPoint(new Buffer("", 32), 0xffffffff); 52 | } 53 | 54 | /** 55 | * @return BufferInterface 56 | */ 57 | public function getTxId(): BufferInterface 58 | { 59 | return $this->hashPrevOutput; 60 | } 61 | 62 | /** 63 | * @return int 64 | */ 65 | public function getVout(): int 66 | { 67 | return $this->nPrevOutput; 68 | } 69 | 70 | /** 71 | * @param OutPointInterface $outPoint 72 | * @return bool 73 | */ 74 | public function equals(OutPointInterface $outPoint): bool 75 | { 76 | $txid = strcmp($this->getTxId()->getBinary(), $outPoint->getTxId()->getBinary()); 77 | if ($txid !== 0) { 78 | return false; 79 | } 80 | 81 | return gmp_cmp($this->getVout(), $outPoint->getVout()) === 0; 82 | } 83 | 84 | /** 85 | * @return BufferInterface 86 | */ 87 | public function getBuffer(): BufferInterface 88 | { 89 | return (new OutPointSerializer())->serialize($this); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Transaction/OutPointInterface.php: -------------------------------------------------------------------------------- 1 | = count($this->tx->getInputs())) { 32 | return Buffer::hex('0100000000000000000000000000000000000000000000000000000000000000', 32); 33 | } 34 | 35 | if (($sighashType & 0x1f) == SigHash::SINGLE) { 36 | if ($inputToSign >= count($this->tx->getOutputs())) { 37 | return Buffer::hex('0100000000000000000000000000000000000000000000000000000000000000', 32); 38 | } 39 | } 40 | 41 | $serializer = new TxSigHashSerializer($this->tx, $txOutScript, $inputToSign, $sighashType); 42 | $sigHashData = new Buffer($serializer->serializeTransaction() . pack('V', $sighashType)); 43 | return Hash::sha256d($sigHashData); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Transaction/SignatureHash/SigHash.php: -------------------------------------------------------------------------------- 1 | tx = $transaction; 28 | } 29 | 30 | /** 31 | * @param ScriptInterface $txOutScript 32 | * @param int $inputToSign 33 | * @param int $sighashType 34 | * @return BufferInterface 35 | */ 36 | abstract public function calculate( 37 | ScriptInterface $txOutScript, 38 | int $inputToSign, 39 | int $sighashType = self::ALL 40 | ): BufferInterface; 41 | } 42 | -------------------------------------------------------------------------------- /src/Transaction/SignatureHash/SigHashInterface.php: -------------------------------------------------------------------------------- 1 | parse($buffer); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Transaction/TransactionOutput.php: -------------------------------------------------------------------------------- 1 | value = $value; 37 | $this->script = $script; 38 | } 39 | 40 | /** 41 | * {@inheritdoc} 42 | * @see TransactionOutputInterface::getValue() 43 | */ 44 | public function getValue(): int 45 | { 46 | return $this->value; 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | * @see TransactionOutputInterface::getScript() 52 | */ 53 | public function getScript(): ScriptInterface 54 | { 55 | return $this->script; 56 | } 57 | 58 | /** 59 | * {@inheritdoc} 60 | * @see TransactionOutputInterface::equals() 61 | */ 62 | public function equals(TransactionOutputInterface $output): bool 63 | { 64 | $script = $this->script->equals($output->getScript()); 65 | if (!$script) { 66 | return false; 67 | } 68 | 69 | return gmp_cmp($this->value, $output->getValue()) === 0; 70 | } 71 | 72 | /** 73 | * {@inheritdoc} 74 | * @see \BitWasp\Bitcoin\SerializableInterface::getBuffer() 75 | */ 76 | public function getBuffer(): BufferInterface 77 | { 78 | return (new TransactionOutputSerializer())->serialize($this); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Transaction/TransactionOutputInterface.php: -------------------------------------------------------------------------------- 1 | outPoint = $outPoint; 30 | $this->prevOut = $prevOut; 31 | } 32 | 33 | /** 34 | * @return OutPointInterface 35 | */ 36 | public function getOutPoint(): OutPointInterface 37 | { 38 | return $this->outPoint; 39 | } 40 | 41 | /** 42 | * @return TransactionOutputInterface 43 | */ 44 | public function getOutput(): TransactionOutputInterface 45 | { 46 | return $this->prevOut; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Utxo/UtxoInterface.php: -------------------------------------------------------------------------------- 1 | /dev/null 8 | if [ $? -ne 0 ]; then 9 | echo "Error running example code: $i"; 10 | exit -1 11 | fi; 12 | done 13 | } 14 | 15 | runDirPHP examples/; 16 | 17 | for i in $(find examples/doc/* -type d); do 18 | runDirPHP "${i}/" 19 | done 20 | --------------------------------------------------------------------------------