├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── composer.json ├── composer.lock ├── examples ├── blake.php ├── generate_address.php └── transaction_listen.php ├── phpunit.xml ├── src └── Decred │ ├── Crypto │ ├── Blake │ │ ├── AbstractState.php │ │ ├── README.md │ │ └── State256.php │ ├── Curve.php │ ├── DecredNetwork.php │ ├── ExtendedKey.php │ ├── Hash.php │ ├── NetworkFactory.php │ └── NetworkInterface.php │ ├── Data │ ├── DataClient.php │ └── Transaction.php │ ├── MainNet.php │ ├── Rate.php │ ├── Rate │ ├── CoinMarketCap.php │ └── RateProviderInterface.php │ └── TestNet.php └── tests ├── Crypto ├── BlakeTest.php ├── ExtendedKeyTest.php └── NetworkTest.php ├── Data ├── DataClientTest.php └── TransactionTest.php ├── Mock └── GeneralNetwork.php ├── RateTest.php └── fixtures ├── dataclient └── api_address_TseQDDfk4xDvz2R92cexf1WaAvUGXEfRJ75_raw.json ├── file311bytes.txt └── file313bytes.txt /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | strategy: 8 | matrix: 9 | php-versions: [7.1, 7.2, 7.3] 10 | name: PHP ${{ matrix.php-versions }} 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v1 14 | 15 | - name: Setup PHP 16 | uses: shivammathur/setup-php@v1 17 | with: 18 | php-version: ${{ matrix.php-versions }} 19 | coverage: xdebug 20 | 21 | - name: Lint 22 | run: find . -name \*.php -exec php -l "{}" \; 23 | 24 | - name: Install Dependencies 25 | run: composer install --ansi --prefer-dist --no-interaction --optimize-autoloader --no-progress 26 | 27 | - name: Test 28 | run: phpunit tests 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | .idea 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2018 Decred developers 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Decred PHP API 2 | 3 | [![Build Status](https://github.com/decred/decred-php-api/workflows/Build%20and%20Test/badge.svg)](https://github.com/decred/decred-php-api/actions) 4 | [![ISC License](https://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) 5 | 6 | 7 | 8 | PHP API for the [Decred](https://decred.org) Cryptocurrency 9 | 10 | ## Installation 11 | 12 | Add composer package to your project 13 | ```bash 14 | composer require decred/decred-php-api 15 | ``` 16 | 17 | Make sure [GMP PHP extesion](https://www.php.net/manual/en/book.gmp.php) is installed. In ubuntu: 18 | ```bash 19 | sudo apt install php7.0-gmp 20 | ``` 21 | 22 | ### From repository 23 | 24 | You also can clone git package with 25 | ```bash 26 | git clone https://github.com/decred/decred-php-api.git 27 | ``` 28 | 29 | But still you will need to fetch library dependencies with [composer](https://getcomposer.org/doc/00-intro.md). 30 | ```bash 31 | composer install --no-dev 32 | ``` 33 | 34 | Don't forget to include composer autoloader. 35 | ```php 36 | include __DIR__.'/../vendor/autoload.php'; 37 | ``` 38 | 39 | ## Usage examples 40 | 41 | Library have wide functionality, so you could find usage examples in `examples` library or looking into PHPUnit tests. 42 | 43 | ### Generating seed 44 | 45 | First of all we need to get Network intance to start working with library. 46 | 47 | ```php 48 | $testnet = \Decred\TestNet::instance(); 49 | ``` 50 | 51 | And mainnet accordingly 52 | ```php 53 | $mainnet = \Decred\MainNet::instance(); 54 | ``` 55 | 56 | Now lets generate a seed, that will be also verified for usage on testnet. 57 | Defaut account and branch address will be derivded to verify the seed. 58 | 59 | ```php 60 | $seed = \Decred\Crypto\ExtendedKey::generateSeed($testnet); 61 | ``` 62 | 63 | ### HD Wallets 64 | 65 | When we have usable seed we can create HD master key. 66 | 67 | ```php 68 | $master = \Decred\Crypto\ExtendedKey::newMaster($seed, $testnet); 69 | ``` 70 | 71 | `newMaster` method will return `ExtendedKey` object, that have variant API for working with HD wallets. 72 | 73 | #### `ExtendedKey::privateChildKey($index)` 74 | 75 | Derives HD private child key from parent HD private key, returns `ExtendedKey` object. 76 | 77 | #### `ExtendedKey::hardenedChildKey($index)` 78 | 79 | Derives HD hardened child key from parent HD private key, returns `ExtendedKey` object. 80 | 81 | Can't be dervied from HD public key. 82 | 83 | #### `ExtendedKey::publicChildKey($index)` 84 | 85 | Derives HD public child key from parent HD private or public key, returns `ExtendedKey` object. 86 | 87 | #### `ExtendedKey::neuter` 88 | 89 | Verify that extended key is public, returns `ExtendedKey` object. 90 | 91 | ### Default account 92 | 93 | Using this basic methods we can derive default account HD private and public keys accourding to [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki). 94 | 95 | HD path (m\44'\42'\0') 96 | 97 | ```php 98 | $defaultAccountPrivateKey = $master 99 | ->hardenedChildKey(44) 100 | ->hardenedChildKey(42) 101 | ->hardenedChildKey(0); 102 | 103 | $defaultAccountPublicKey = $master->neuter(); 104 | ``` 105 | 106 | `ExtendedKey` implements `__toString()` method, so you can easily get Base58 representation of HD key. 107 | 108 | ```php 109 | echo sprintf("Default account HD private key: %s\n", $defaultAccountPrivateKey); 110 | echo sprintf("Default account HD public key: %s\n", $defaultAccountPublicKey); 111 | ``` 112 | 113 | From default account we can derive 0 branch and 0 index and the get default address. 114 | 115 | ```php 116 | $defaultAddress = $defaultAccountPublicKey 117 | ->publicChildKey(0) 118 | ->publicChildKey(0) 119 | ->getAddress(); 120 | 121 | echo sprintf("Default address: %s\n", $defaultAddress); 122 | ``` 123 | 124 | 125 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "decred/decred-php-api", 3 | "description": "PHP API for the Decred Cryptocurrency", 4 | "license": "ISC", 5 | "keywords": ["decred", "crypto", "dcr", "bitcoin"], 6 | "autoload": { 7 | "psr-4": { 8 | "Decred\\": "src/Decred", 9 | "Decred\\Tests\\": "tests" 10 | } 11 | }, 12 | "require": { 13 | "php": ">=5.6", 14 | "mdanter/ecc": "^0.4.0", 15 | "guzzlehttp/guzzle": "^6.3", 16 | "stephenhill/base58": "^1.1" 17 | }, 18 | "require-dev": { 19 | "phpunit/phpunit": "^5", 20 | "phpdocumentor/reflection-docblock": "^3", 21 | "phpunit/php-code-coverage": "^4.0", 22 | "phpunit/php-token-stream": "^1", 23 | "phpspec/prophecy": "^1.7" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "26b05b4a4a47da6729e756ae9abec742", 8 | "content-hash": "27f44fb075e44b0576e9a1d1d40765fc", 9 | "packages": [ 10 | { 11 | "name": "fgrosse/phpasn1", 12 | "version": "v1.5.4", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/fgrosse/PHPASN1.git", 16 | "reference": "4fe0afb91b4ce3ca08c63d9cf31cec1150828e97" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/4fe0afb91b4ce3ca08c63d9cf31cec1150828e97", 21 | "reference": "4fe0afb91b4ce3ca08c63d9cf31cec1150828e97", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "ext-gmp": "*", 26 | "php": ">=5.6.0" 27 | }, 28 | "require-dev": { 29 | "php-coveralls/php-coveralls": "^1.0|^2.0", 30 | "phpunit/phpunit": "~4.5" 31 | }, 32 | "suggest": { 33 | "php-curl": "For loading OID information from the web if they have not bee defined statically" 34 | }, 35 | "type": "library", 36 | "extra": { 37 | "branch-alias": { 38 | "dev-master": "1.5.x-dev" 39 | } 40 | }, 41 | "autoload": { 42 | "psr-4": { 43 | "FG\\": "lib/" 44 | } 45 | }, 46 | "notification-url": "https://packagist.org/downloads/", 47 | "license": [ 48 | "MIT" 49 | ], 50 | "authors": [ 51 | { 52 | "name": "Friedrich Große", 53 | "role": "Author", 54 | "email": "friedrich.grosse@gmail.com", 55 | "homepage": "https://github.com/FGrosse" 56 | }, 57 | { 58 | "name": "All contributors", 59 | "homepage": "https://github.com/FGrosse/PHPASN1/contributors" 60 | } 61 | ], 62 | "description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.", 63 | "homepage": "https://github.com/FGrosse/PHPASN1", 64 | "keywords": [ 65 | "DER", 66 | "asn.1", 67 | "asn1", 68 | "ber", 69 | "binary", 70 | "decoding", 71 | "encoding", 72 | "x.509", 73 | "x.690", 74 | "x509", 75 | "x690" 76 | ], 77 | "time": "2018-12-02 01:31:42" 78 | }, 79 | { 80 | "name": "guzzlehttp/guzzle", 81 | "version": "6.3.3", 82 | "source": { 83 | "type": "git", 84 | "url": "https://github.com/guzzle/guzzle.git", 85 | "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" 86 | }, 87 | "dist": { 88 | "type": "zip", 89 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", 90 | "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", 91 | "shasum": "" 92 | }, 93 | "require": { 94 | "guzzlehttp/promises": "^1.0", 95 | "guzzlehttp/psr7": "^1.4", 96 | "php": ">=5.5" 97 | }, 98 | "require-dev": { 99 | "ext-curl": "*", 100 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", 101 | "psr/log": "^1.0" 102 | }, 103 | "suggest": { 104 | "psr/log": "Required for using the Log middleware" 105 | }, 106 | "type": "library", 107 | "extra": { 108 | "branch-alias": { 109 | "dev-master": "6.3-dev" 110 | } 111 | }, 112 | "autoload": { 113 | "files": [ 114 | "src/functions_include.php" 115 | ], 116 | "psr-4": { 117 | "GuzzleHttp\\": "src/" 118 | } 119 | }, 120 | "notification-url": "https://packagist.org/downloads/", 121 | "license": [ 122 | "MIT" 123 | ], 124 | "authors": [ 125 | { 126 | "name": "Michael Dowling", 127 | "email": "mtdowling@gmail.com", 128 | "homepage": "https://github.com/mtdowling" 129 | } 130 | ], 131 | "description": "Guzzle is a PHP HTTP client library", 132 | "homepage": "http://guzzlephp.org/", 133 | "keywords": [ 134 | "client", 135 | "curl", 136 | "framework", 137 | "http", 138 | "http client", 139 | "rest", 140 | "web service" 141 | ], 142 | "time": "2018-04-22 15:46:56" 143 | }, 144 | { 145 | "name": "guzzlehttp/promises", 146 | "version": "v1.3.1", 147 | "source": { 148 | "type": "git", 149 | "url": "https://github.com/guzzle/promises.git", 150 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" 151 | }, 152 | "dist": { 153 | "type": "zip", 154 | "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", 155 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", 156 | "shasum": "" 157 | }, 158 | "require": { 159 | "php": ">=5.5.0" 160 | }, 161 | "require-dev": { 162 | "phpunit/phpunit": "^4.0" 163 | }, 164 | "type": "library", 165 | "extra": { 166 | "branch-alias": { 167 | "dev-master": "1.4-dev" 168 | } 169 | }, 170 | "autoload": { 171 | "psr-4": { 172 | "GuzzleHttp\\Promise\\": "src/" 173 | }, 174 | "files": [ 175 | "src/functions_include.php" 176 | ] 177 | }, 178 | "notification-url": "https://packagist.org/downloads/", 179 | "license": [ 180 | "MIT" 181 | ], 182 | "authors": [ 183 | { 184 | "name": "Michael Dowling", 185 | "email": "mtdowling@gmail.com", 186 | "homepage": "https://github.com/mtdowling" 187 | } 188 | ], 189 | "description": "Guzzle promises library", 190 | "keywords": [ 191 | "promise" 192 | ], 193 | "time": "2016-12-20 10:07:11" 194 | }, 195 | { 196 | "name": "guzzlehttp/psr7", 197 | "version": "1.6.1", 198 | "source": { 199 | "type": "git", 200 | "url": "https://github.com/guzzle/psr7.git", 201 | "reference": "239400de7a173fe9901b9ac7c06497751f00727a" 202 | }, 203 | "dist": { 204 | "type": "zip", 205 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a", 206 | "reference": "239400de7a173fe9901b9ac7c06497751f00727a", 207 | "shasum": "" 208 | }, 209 | "require": { 210 | "php": ">=5.4.0", 211 | "psr/http-message": "~1.0", 212 | "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" 213 | }, 214 | "provide": { 215 | "psr/http-message-implementation": "1.0" 216 | }, 217 | "require-dev": { 218 | "ext-zlib": "*", 219 | "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" 220 | }, 221 | "suggest": { 222 | "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses" 223 | }, 224 | "type": "library", 225 | "extra": { 226 | "branch-alias": { 227 | "dev-master": "1.6-dev" 228 | } 229 | }, 230 | "autoload": { 231 | "psr-4": { 232 | "GuzzleHttp\\Psr7\\": "src/" 233 | }, 234 | "files": [ 235 | "src/functions_include.php" 236 | ] 237 | }, 238 | "notification-url": "https://packagist.org/downloads/", 239 | "license": [ 240 | "MIT" 241 | ], 242 | "authors": [ 243 | { 244 | "name": "Michael Dowling", 245 | "email": "mtdowling@gmail.com", 246 | "homepage": "https://github.com/mtdowling" 247 | }, 248 | { 249 | "name": "Tobias Schultze", 250 | "homepage": "https://github.com/Tobion" 251 | } 252 | ], 253 | "description": "PSR-7 message implementation that also provides common utility methods", 254 | "keywords": [ 255 | "http", 256 | "message", 257 | "psr-7", 258 | "request", 259 | "response", 260 | "stream", 261 | "uri", 262 | "url" 263 | ], 264 | "time": "2019-07-01 23:21:34" 265 | }, 266 | { 267 | "name": "mdanter/ecc", 268 | "version": "v0.4.3", 269 | "source": { 270 | "type": "git", 271 | "url": "https://github.com/phpecc/phpecc.git", 272 | "reference": "fa3405da1b2bb4772a0c908c65b0c3e9dde4ccfd" 273 | }, 274 | "dist": { 275 | "type": "zip", 276 | "url": "https://api.github.com/repos/phpecc/phpecc/zipball/fa3405da1b2bb4772a0c908c65b0c3e9dde4ccfd", 277 | "reference": "fa3405da1b2bb4772a0c908c65b0c3e9dde4ccfd", 278 | "shasum": "" 279 | }, 280 | "require": { 281 | "ext-gmp": "*", 282 | "fgrosse/phpasn1": "~1.5", 283 | "paragonie/random_compat": "^1|^2", 284 | "php": ">=5.6.0" 285 | }, 286 | "require-dev": { 287 | "phpunit/phpunit": "~4.1|~5.0", 288 | "squizlabs/php_codesniffer": "~2", 289 | "symfony/yaml": "~2.6|~3.0" 290 | }, 291 | "type": "library", 292 | "autoload": { 293 | "psr-4": { 294 | "Mdanter\\Ecc\\": "src/" 295 | } 296 | }, 297 | "notification-url": "https://packagist.org/downloads/", 298 | "license": [ 299 | "MIT" 300 | ], 301 | "authors": [ 302 | { 303 | "name": "Matyas Danter", 304 | "homepage": "http://matejdanter.com/", 305 | "role": "Author" 306 | }, 307 | { 308 | "name": "Thibaud Fabre", 309 | "email": "thibaud@aztech.io", 310 | "homepage": "http://aztech.io", 311 | "role": "Maintainer" 312 | } 313 | ], 314 | "description": "PHP Elliptic Curve Cryptography library", 315 | "homepage": "https://github.com/phpecc/phpecc", 316 | "keywords": [ 317 | "Diffie", 318 | "ECDSA", 319 | "Hellman", 320 | "curve", 321 | "ecdh", 322 | "elliptic", 323 | "nistp192", 324 | "nistp224", 325 | "nistp256", 326 | "nistp521", 327 | "phpecc", 328 | "secp256k1" 329 | ], 330 | "time": "2017-09-22 15:12:06" 331 | }, 332 | { 333 | "name": "paragonie/random_compat", 334 | "version": "v2.0.18", 335 | "source": { 336 | "type": "git", 337 | "url": "https://github.com/paragonie/random_compat.git", 338 | "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db" 339 | }, 340 | "dist": { 341 | "type": "zip", 342 | "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", 343 | "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", 344 | "shasum": "" 345 | }, 346 | "require": { 347 | "php": ">=5.2.0" 348 | }, 349 | "require-dev": { 350 | "phpunit/phpunit": "4.*|5.*" 351 | }, 352 | "suggest": { 353 | "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." 354 | }, 355 | "type": "library", 356 | "autoload": { 357 | "files": [ 358 | "lib/random.php" 359 | ] 360 | }, 361 | "notification-url": "https://packagist.org/downloads/", 362 | "license": [ 363 | "MIT" 364 | ], 365 | "authors": [ 366 | { 367 | "name": "Paragon Initiative Enterprises", 368 | "email": "security@paragonie.com", 369 | "homepage": "https://paragonie.com" 370 | } 371 | ], 372 | "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", 373 | "keywords": [ 374 | "csprng", 375 | "polyfill", 376 | "pseudorandom", 377 | "random" 378 | ], 379 | "time": "2019-01-03 20:59:08" 380 | }, 381 | { 382 | "name": "psr/http-message", 383 | "version": "1.0.1", 384 | "source": { 385 | "type": "git", 386 | "url": "https://github.com/php-fig/http-message.git", 387 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" 388 | }, 389 | "dist": { 390 | "type": "zip", 391 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", 392 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", 393 | "shasum": "" 394 | }, 395 | "require": { 396 | "php": ">=5.3.0" 397 | }, 398 | "type": "library", 399 | "extra": { 400 | "branch-alias": { 401 | "dev-master": "1.0.x-dev" 402 | } 403 | }, 404 | "autoload": { 405 | "psr-4": { 406 | "Psr\\Http\\Message\\": "src/" 407 | } 408 | }, 409 | "notification-url": "https://packagist.org/downloads/", 410 | "license": [ 411 | "MIT" 412 | ], 413 | "authors": [ 414 | { 415 | "name": "PHP-FIG", 416 | "homepage": "http://www.php-fig.org/" 417 | } 418 | ], 419 | "description": "Common interface for HTTP messages", 420 | "homepage": "https://github.com/php-fig/http-message", 421 | "keywords": [ 422 | "http", 423 | "http-message", 424 | "psr", 425 | "psr-7", 426 | "request", 427 | "response" 428 | ], 429 | "time": "2016-08-06 14:39:51" 430 | }, 431 | { 432 | "name": "ralouphie/getallheaders", 433 | "version": "3.0.3", 434 | "source": { 435 | "type": "git", 436 | "url": "https://github.com/ralouphie/getallheaders.git", 437 | "reference": "120b605dfeb996808c31b6477290a714d356e822" 438 | }, 439 | "dist": { 440 | "type": "zip", 441 | "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", 442 | "reference": "120b605dfeb996808c31b6477290a714d356e822", 443 | "shasum": "" 444 | }, 445 | "require": { 446 | "php": ">=5.6" 447 | }, 448 | "require-dev": { 449 | "php-coveralls/php-coveralls": "^2.1", 450 | "phpunit/phpunit": "^5 || ^6.5" 451 | }, 452 | "type": "library", 453 | "autoload": { 454 | "files": [ 455 | "src/getallheaders.php" 456 | ] 457 | }, 458 | "notification-url": "https://packagist.org/downloads/", 459 | "license": [ 460 | "MIT" 461 | ], 462 | "authors": [ 463 | { 464 | "name": "Ralph Khattar", 465 | "email": "ralph.khattar@gmail.com" 466 | } 467 | ], 468 | "description": "A polyfill for getallheaders.", 469 | "time": "2019-03-08 08:55:37" 470 | }, 471 | { 472 | "name": "stephenhill/base58", 473 | "version": "v1.1.4", 474 | "source": { 475 | "type": "git", 476 | "url": "https://github.com/stephen-hill/base58php.git", 477 | "reference": "75ef7128303def7629d2a4bf9ea9dc378140507c" 478 | }, 479 | "dist": { 480 | "type": "zip", 481 | "url": "https://api.github.com/repos/stephen-hill/base58php/zipball/75ef7128303def7629d2a4bf9ea9dc378140507c", 482 | "reference": "75ef7128303def7629d2a4bf9ea9dc378140507c", 483 | "shasum": "" 484 | }, 485 | "require-dev": { 486 | "athletic/athletic": "~0.1", 487 | "phpunit/phpunit": "4.*" 488 | }, 489 | "type": "library", 490 | "autoload": { 491 | "psr-4": { 492 | "StephenHill\\": "src/", 493 | "StephenHill\\Benchmarks\\": "benchmarks/" 494 | } 495 | }, 496 | "notification-url": "https://packagist.org/downloads/", 497 | "license": [ 498 | "MIT" 499 | ], 500 | "authors": [ 501 | { 502 | "name": "Stephen Hill", 503 | "email": "stephen@gatekiller.co.uk" 504 | } 505 | ], 506 | "description": "Base58 Encoding and Decoding Library for PHP", 507 | "time": "2016-07-20 20:34:07" 508 | } 509 | ], 510 | "packages-dev": [ 511 | { 512 | "name": "doctrine/instantiator", 513 | "version": "1.2.0", 514 | "source": { 515 | "type": "git", 516 | "url": "https://github.com/doctrine/instantiator.git", 517 | "reference": "a2c590166b2133a4633738648b6b064edae0814a" 518 | }, 519 | "dist": { 520 | "type": "zip", 521 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", 522 | "reference": "a2c590166b2133a4633738648b6b064edae0814a", 523 | "shasum": "" 524 | }, 525 | "require": { 526 | "php": "^7.1" 527 | }, 528 | "require-dev": { 529 | "doctrine/coding-standard": "^6.0", 530 | "ext-pdo": "*", 531 | "ext-phar": "*", 532 | "phpbench/phpbench": "^0.13", 533 | "phpstan/phpstan-phpunit": "^0.11", 534 | "phpstan/phpstan-shim": "^0.11", 535 | "phpunit/phpunit": "^7.0" 536 | }, 537 | "type": "library", 538 | "extra": { 539 | "branch-alias": { 540 | "dev-master": "1.2.x-dev" 541 | } 542 | }, 543 | "autoload": { 544 | "psr-4": { 545 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 546 | } 547 | }, 548 | "notification-url": "https://packagist.org/downloads/", 549 | "license": [ 550 | "MIT" 551 | ], 552 | "authors": [ 553 | { 554 | "name": "Marco Pivetta", 555 | "email": "ocramius@gmail.com", 556 | "homepage": "http://ocramius.github.com/" 557 | } 558 | ], 559 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 560 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html", 561 | "keywords": [ 562 | "constructor", 563 | "instantiate" 564 | ], 565 | "time": "2019-03-17 17:37:11" 566 | }, 567 | { 568 | "name": "myclabs/deep-copy", 569 | "version": "1.9.1", 570 | "source": { 571 | "type": "git", 572 | "url": "https://github.com/myclabs/DeepCopy.git", 573 | "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72" 574 | }, 575 | "dist": { 576 | "type": "zip", 577 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", 578 | "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", 579 | "shasum": "" 580 | }, 581 | "require": { 582 | "php": "^7.1" 583 | }, 584 | "replace": { 585 | "myclabs/deep-copy": "self.version" 586 | }, 587 | "require-dev": { 588 | "doctrine/collections": "^1.0", 589 | "doctrine/common": "^2.6", 590 | "phpunit/phpunit": "^7.1" 591 | }, 592 | "type": "library", 593 | "autoload": { 594 | "psr-4": { 595 | "DeepCopy\\": "src/DeepCopy/" 596 | }, 597 | "files": [ 598 | "src/DeepCopy/deep_copy.php" 599 | ] 600 | }, 601 | "notification-url": "https://packagist.org/downloads/", 602 | "license": [ 603 | "MIT" 604 | ], 605 | "description": "Create deep copies (clones) of your objects", 606 | "keywords": [ 607 | "clone", 608 | "copy", 609 | "duplicate", 610 | "object", 611 | "object graph" 612 | ], 613 | "time": "2019-04-07 13:18:21" 614 | }, 615 | { 616 | "name": "phpdocumentor/reflection-common", 617 | "version": "1.0.1", 618 | "source": { 619 | "type": "git", 620 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 621 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" 622 | }, 623 | "dist": { 624 | "type": "zip", 625 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", 626 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", 627 | "shasum": "" 628 | }, 629 | "require": { 630 | "php": ">=5.5" 631 | }, 632 | "require-dev": { 633 | "phpunit/phpunit": "^4.6" 634 | }, 635 | "type": "library", 636 | "extra": { 637 | "branch-alias": { 638 | "dev-master": "1.0.x-dev" 639 | } 640 | }, 641 | "autoload": { 642 | "psr-4": { 643 | "phpDocumentor\\Reflection\\": [ 644 | "src" 645 | ] 646 | } 647 | }, 648 | "notification-url": "https://packagist.org/downloads/", 649 | "license": [ 650 | "MIT" 651 | ], 652 | "authors": [ 653 | { 654 | "name": "Jaap van Otterdijk", 655 | "email": "opensource@ijaap.nl" 656 | } 657 | ], 658 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 659 | "homepage": "http://www.phpdoc.org", 660 | "keywords": [ 661 | "FQSEN", 662 | "phpDocumentor", 663 | "phpdoc", 664 | "reflection", 665 | "static analysis" 666 | ], 667 | "time": "2017-09-11 18:02:19" 668 | }, 669 | { 670 | "name": "phpdocumentor/reflection-docblock", 671 | "version": "3.3.2", 672 | "source": { 673 | "type": "git", 674 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 675 | "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2" 676 | }, 677 | "dist": { 678 | "type": "zip", 679 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2", 680 | "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2", 681 | "shasum": "" 682 | }, 683 | "require": { 684 | "php": "^5.6 || ^7.0", 685 | "phpdocumentor/reflection-common": "^1.0.0", 686 | "phpdocumentor/type-resolver": "^0.4.0", 687 | "webmozart/assert": "^1.0" 688 | }, 689 | "require-dev": { 690 | "mockery/mockery": "^0.9.4", 691 | "phpunit/phpunit": "^4.4" 692 | }, 693 | "type": "library", 694 | "autoload": { 695 | "psr-4": { 696 | "phpDocumentor\\Reflection\\": [ 697 | "src/" 698 | ] 699 | } 700 | }, 701 | "notification-url": "https://packagist.org/downloads/", 702 | "license": [ 703 | "MIT" 704 | ], 705 | "authors": [ 706 | { 707 | "name": "Mike van Riel", 708 | "email": "me@mikevanriel.com" 709 | } 710 | ], 711 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 712 | "time": "2017-11-10 14:09:06" 713 | }, 714 | { 715 | "name": "phpdocumentor/type-resolver", 716 | "version": "0.4.0", 717 | "source": { 718 | "type": "git", 719 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 720 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" 721 | }, 722 | "dist": { 723 | "type": "zip", 724 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", 725 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", 726 | "shasum": "" 727 | }, 728 | "require": { 729 | "php": "^5.5 || ^7.0", 730 | "phpdocumentor/reflection-common": "^1.0" 731 | }, 732 | "require-dev": { 733 | "mockery/mockery": "^0.9.4", 734 | "phpunit/phpunit": "^5.2||^4.8.24" 735 | }, 736 | "type": "library", 737 | "extra": { 738 | "branch-alias": { 739 | "dev-master": "1.0.x-dev" 740 | } 741 | }, 742 | "autoload": { 743 | "psr-4": { 744 | "phpDocumentor\\Reflection\\": [ 745 | "src/" 746 | ] 747 | } 748 | }, 749 | "notification-url": "https://packagist.org/downloads/", 750 | "license": [ 751 | "MIT" 752 | ], 753 | "authors": [ 754 | { 755 | "name": "Mike van Riel", 756 | "email": "me@mikevanriel.com" 757 | } 758 | ], 759 | "time": "2017-07-14 14:27:02" 760 | }, 761 | { 762 | "name": "phpspec/prophecy", 763 | "version": "1.8.1", 764 | "source": { 765 | "type": "git", 766 | "url": "https://github.com/phpspec/prophecy.git", 767 | "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76" 768 | }, 769 | "dist": { 770 | "type": "zip", 771 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76", 772 | "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76", 773 | "shasum": "" 774 | }, 775 | "require": { 776 | "doctrine/instantiator": "^1.0.2", 777 | "php": "^5.3|^7.0", 778 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", 779 | "sebastian/comparator": "^1.1|^2.0|^3.0", 780 | "sebastian/recursion-context": "^1.0|^2.0|^3.0" 781 | }, 782 | "require-dev": { 783 | "phpspec/phpspec": "^2.5|^3.2", 784 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" 785 | }, 786 | "type": "library", 787 | "extra": { 788 | "branch-alias": { 789 | "dev-master": "1.8.x-dev" 790 | } 791 | }, 792 | "autoload": { 793 | "psr-4": { 794 | "Prophecy\\": "src/Prophecy" 795 | } 796 | }, 797 | "notification-url": "https://packagist.org/downloads/", 798 | "license": [ 799 | "MIT" 800 | ], 801 | "authors": [ 802 | { 803 | "name": "Konstantin Kudryashov", 804 | "email": "ever.zet@gmail.com", 805 | "homepage": "http://everzet.com" 806 | }, 807 | { 808 | "name": "Marcello Duarte", 809 | "email": "marcello.duarte@gmail.com" 810 | } 811 | ], 812 | "description": "Highly opinionated mocking framework for PHP 5.3+", 813 | "homepage": "https://github.com/phpspec/prophecy", 814 | "keywords": [ 815 | "Double", 816 | "Dummy", 817 | "fake", 818 | "mock", 819 | "spy", 820 | "stub" 821 | ], 822 | "time": "2019-06-13 12:50:23" 823 | }, 824 | { 825 | "name": "phpunit/php-code-coverage", 826 | "version": "4.0.8", 827 | "source": { 828 | "type": "git", 829 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 830 | "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" 831 | }, 832 | "dist": { 833 | "type": "zip", 834 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", 835 | "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", 836 | "shasum": "" 837 | }, 838 | "require": { 839 | "ext-dom": "*", 840 | "ext-xmlwriter": "*", 841 | "php": "^5.6 || ^7.0", 842 | "phpunit/php-file-iterator": "^1.3", 843 | "phpunit/php-text-template": "^1.2", 844 | "phpunit/php-token-stream": "^1.4.2 || ^2.0", 845 | "sebastian/code-unit-reverse-lookup": "^1.0", 846 | "sebastian/environment": "^1.3.2 || ^2.0", 847 | "sebastian/version": "^1.0 || ^2.0" 848 | }, 849 | "require-dev": { 850 | "ext-xdebug": "^2.1.4", 851 | "phpunit/phpunit": "^5.7" 852 | }, 853 | "suggest": { 854 | "ext-xdebug": "^2.5.1" 855 | }, 856 | "type": "library", 857 | "extra": { 858 | "branch-alias": { 859 | "dev-master": "4.0.x-dev" 860 | } 861 | }, 862 | "autoload": { 863 | "classmap": [ 864 | "src/" 865 | ] 866 | }, 867 | "notification-url": "https://packagist.org/downloads/", 868 | "license": [ 869 | "BSD-3-Clause" 870 | ], 871 | "authors": [ 872 | { 873 | "name": "Sebastian Bergmann", 874 | "role": "lead", 875 | "email": "sb@sebastian-bergmann.de" 876 | } 877 | ], 878 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 879 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 880 | "keywords": [ 881 | "coverage", 882 | "testing", 883 | "xunit" 884 | ], 885 | "time": "2017-04-02 07:44:40" 886 | }, 887 | { 888 | "name": "phpunit/php-file-iterator", 889 | "version": "1.4.5", 890 | "source": { 891 | "type": "git", 892 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 893 | "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" 894 | }, 895 | "dist": { 896 | "type": "zip", 897 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", 898 | "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", 899 | "shasum": "" 900 | }, 901 | "require": { 902 | "php": ">=5.3.3" 903 | }, 904 | "type": "library", 905 | "extra": { 906 | "branch-alias": { 907 | "dev-master": "1.4.x-dev" 908 | } 909 | }, 910 | "autoload": { 911 | "classmap": [ 912 | "src/" 913 | ] 914 | }, 915 | "notification-url": "https://packagist.org/downloads/", 916 | "license": [ 917 | "BSD-3-Clause" 918 | ], 919 | "authors": [ 920 | { 921 | "name": "Sebastian Bergmann", 922 | "email": "sb@sebastian-bergmann.de", 923 | "role": "lead" 924 | } 925 | ], 926 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 927 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 928 | "keywords": [ 929 | "filesystem", 930 | "iterator" 931 | ], 932 | "time": "2017-11-27 13:52:08" 933 | }, 934 | { 935 | "name": "phpunit/php-text-template", 936 | "version": "1.2.1", 937 | "source": { 938 | "type": "git", 939 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 940 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 941 | }, 942 | "dist": { 943 | "type": "zip", 944 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 945 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 946 | "shasum": "" 947 | }, 948 | "require": { 949 | "php": ">=5.3.3" 950 | }, 951 | "type": "library", 952 | "autoload": { 953 | "classmap": [ 954 | "src/" 955 | ] 956 | }, 957 | "notification-url": "https://packagist.org/downloads/", 958 | "license": [ 959 | "BSD-3-Clause" 960 | ], 961 | "authors": [ 962 | { 963 | "name": "Sebastian Bergmann", 964 | "email": "sebastian@phpunit.de", 965 | "role": "lead" 966 | } 967 | ], 968 | "description": "Simple template engine.", 969 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 970 | "keywords": [ 971 | "template" 972 | ], 973 | "time": "2015-06-21 13:50:34" 974 | }, 975 | { 976 | "name": "phpunit/php-timer", 977 | "version": "1.0.9", 978 | "source": { 979 | "type": "git", 980 | "url": "https://github.com/sebastianbergmann/php-timer.git", 981 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" 982 | }, 983 | "dist": { 984 | "type": "zip", 985 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 986 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 987 | "shasum": "" 988 | }, 989 | "require": { 990 | "php": "^5.3.3 || ^7.0" 991 | }, 992 | "require-dev": { 993 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" 994 | }, 995 | "type": "library", 996 | "extra": { 997 | "branch-alias": { 998 | "dev-master": "1.0-dev" 999 | } 1000 | }, 1001 | "autoload": { 1002 | "classmap": [ 1003 | "src/" 1004 | ] 1005 | }, 1006 | "notification-url": "https://packagist.org/downloads/", 1007 | "license": [ 1008 | "BSD-3-Clause" 1009 | ], 1010 | "authors": [ 1011 | { 1012 | "name": "Sebastian Bergmann", 1013 | "email": "sb@sebastian-bergmann.de", 1014 | "role": "lead" 1015 | } 1016 | ], 1017 | "description": "Utility class for timing", 1018 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 1019 | "keywords": [ 1020 | "timer" 1021 | ], 1022 | "time": "2017-02-26 11:10:40" 1023 | }, 1024 | { 1025 | "name": "phpunit/php-token-stream", 1026 | "version": "1.4.12", 1027 | "source": { 1028 | "type": "git", 1029 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 1030 | "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16" 1031 | }, 1032 | "dist": { 1033 | "type": "zip", 1034 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16", 1035 | "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16", 1036 | "shasum": "" 1037 | }, 1038 | "require": { 1039 | "ext-tokenizer": "*", 1040 | "php": ">=5.3.3" 1041 | }, 1042 | "require-dev": { 1043 | "phpunit/phpunit": "~4.2" 1044 | }, 1045 | "type": "library", 1046 | "extra": { 1047 | "branch-alias": { 1048 | "dev-master": "1.4-dev" 1049 | } 1050 | }, 1051 | "autoload": { 1052 | "classmap": [ 1053 | "src/" 1054 | ] 1055 | }, 1056 | "notification-url": "https://packagist.org/downloads/", 1057 | "license": [ 1058 | "BSD-3-Clause" 1059 | ], 1060 | "authors": [ 1061 | { 1062 | "name": "Sebastian Bergmann", 1063 | "email": "sebastian@phpunit.de" 1064 | } 1065 | ], 1066 | "description": "Wrapper around PHP's tokenizer extension.", 1067 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 1068 | "keywords": [ 1069 | "tokenizer" 1070 | ], 1071 | "time": "2017-12-04 08:55:13" 1072 | }, 1073 | { 1074 | "name": "phpunit/phpunit", 1075 | "version": "5.7.27", 1076 | "source": { 1077 | "type": "git", 1078 | "url": "https://github.com/sebastianbergmann/phpunit.git", 1079 | "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c" 1080 | }, 1081 | "dist": { 1082 | "type": "zip", 1083 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", 1084 | "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", 1085 | "shasum": "" 1086 | }, 1087 | "require": { 1088 | "ext-dom": "*", 1089 | "ext-json": "*", 1090 | "ext-libxml": "*", 1091 | "ext-mbstring": "*", 1092 | "ext-xml": "*", 1093 | "myclabs/deep-copy": "~1.3", 1094 | "php": "^5.6 || ^7.0", 1095 | "phpspec/prophecy": "^1.6.2", 1096 | "phpunit/php-code-coverage": "^4.0.4", 1097 | "phpunit/php-file-iterator": "~1.4", 1098 | "phpunit/php-text-template": "~1.2", 1099 | "phpunit/php-timer": "^1.0.6", 1100 | "phpunit/phpunit-mock-objects": "^3.2", 1101 | "sebastian/comparator": "^1.2.4", 1102 | "sebastian/diff": "^1.4.3", 1103 | "sebastian/environment": "^1.3.4 || ^2.0", 1104 | "sebastian/exporter": "~2.0", 1105 | "sebastian/global-state": "^1.1", 1106 | "sebastian/object-enumerator": "~2.0", 1107 | "sebastian/resource-operations": "~1.0", 1108 | "sebastian/version": "^1.0.6|^2.0.1", 1109 | "symfony/yaml": "~2.1|~3.0|~4.0" 1110 | }, 1111 | "conflict": { 1112 | "phpdocumentor/reflection-docblock": "3.0.2" 1113 | }, 1114 | "require-dev": { 1115 | "ext-pdo": "*" 1116 | }, 1117 | "suggest": { 1118 | "ext-xdebug": "*", 1119 | "phpunit/php-invoker": "~1.1" 1120 | }, 1121 | "bin": [ 1122 | "phpunit" 1123 | ], 1124 | "type": "library", 1125 | "extra": { 1126 | "branch-alias": { 1127 | "dev-master": "5.7.x-dev" 1128 | } 1129 | }, 1130 | "autoload": { 1131 | "classmap": [ 1132 | "src/" 1133 | ] 1134 | }, 1135 | "notification-url": "https://packagist.org/downloads/", 1136 | "license": [ 1137 | "BSD-3-Clause" 1138 | ], 1139 | "authors": [ 1140 | { 1141 | "name": "Sebastian Bergmann", 1142 | "role": "lead", 1143 | "email": "sebastian@phpunit.de" 1144 | } 1145 | ], 1146 | "description": "The PHP Unit Testing framework.", 1147 | "homepage": "https://phpunit.de/", 1148 | "keywords": [ 1149 | "phpunit", 1150 | "testing", 1151 | "xunit" 1152 | ], 1153 | "time": "2018-02-01 05:50:59" 1154 | }, 1155 | { 1156 | "name": "phpunit/phpunit-mock-objects", 1157 | "version": "3.4.4", 1158 | "source": { 1159 | "type": "git", 1160 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 1161 | "reference": "a23b761686d50a560cc56233b9ecf49597cc9118" 1162 | }, 1163 | "dist": { 1164 | "type": "zip", 1165 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118", 1166 | "reference": "a23b761686d50a560cc56233b9ecf49597cc9118", 1167 | "shasum": "" 1168 | }, 1169 | "require": { 1170 | "doctrine/instantiator": "^1.0.2", 1171 | "php": "^5.6 || ^7.0", 1172 | "phpunit/php-text-template": "^1.2", 1173 | "sebastian/exporter": "^1.2 || ^2.0" 1174 | }, 1175 | "conflict": { 1176 | "phpunit/phpunit": "<5.4.0" 1177 | }, 1178 | "require-dev": { 1179 | "phpunit/phpunit": "^5.4" 1180 | }, 1181 | "suggest": { 1182 | "ext-soap": "*" 1183 | }, 1184 | "type": "library", 1185 | "extra": { 1186 | "branch-alias": { 1187 | "dev-master": "3.2.x-dev" 1188 | } 1189 | }, 1190 | "autoload": { 1191 | "classmap": [ 1192 | "src/" 1193 | ] 1194 | }, 1195 | "notification-url": "https://packagist.org/downloads/", 1196 | "license": [ 1197 | "BSD-3-Clause" 1198 | ], 1199 | "authors": [ 1200 | { 1201 | "name": "Sebastian Bergmann", 1202 | "email": "sb@sebastian-bergmann.de", 1203 | "role": "lead" 1204 | } 1205 | ], 1206 | "description": "Mock Object library for PHPUnit", 1207 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 1208 | "keywords": [ 1209 | "mock", 1210 | "xunit" 1211 | ], 1212 | "abandoned": true, 1213 | "time": "2017-06-30 09:13:00" 1214 | }, 1215 | { 1216 | "name": "sebastian/code-unit-reverse-lookup", 1217 | "version": "1.0.1", 1218 | "source": { 1219 | "type": "git", 1220 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 1221 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" 1222 | }, 1223 | "dist": { 1224 | "type": "zip", 1225 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 1226 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 1227 | "shasum": "" 1228 | }, 1229 | "require": { 1230 | "php": "^5.6 || ^7.0" 1231 | }, 1232 | "require-dev": { 1233 | "phpunit/phpunit": "^5.7 || ^6.0" 1234 | }, 1235 | "type": "library", 1236 | "extra": { 1237 | "branch-alias": { 1238 | "dev-master": "1.0.x-dev" 1239 | } 1240 | }, 1241 | "autoload": { 1242 | "classmap": [ 1243 | "src/" 1244 | ] 1245 | }, 1246 | "notification-url": "https://packagist.org/downloads/", 1247 | "license": [ 1248 | "BSD-3-Clause" 1249 | ], 1250 | "authors": [ 1251 | { 1252 | "name": "Sebastian Bergmann", 1253 | "email": "sebastian@phpunit.de" 1254 | } 1255 | ], 1256 | "description": "Looks up which function or method a line of code belongs to", 1257 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 1258 | "time": "2017-03-04 06:30:41" 1259 | }, 1260 | { 1261 | "name": "sebastian/comparator", 1262 | "version": "1.2.4", 1263 | "source": { 1264 | "type": "git", 1265 | "url": "https://github.com/sebastianbergmann/comparator.git", 1266 | "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" 1267 | }, 1268 | "dist": { 1269 | "type": "zip", 1270 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", 1271 | "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", 1272 | "shasum": "" 1273 | }, 1274 | "require": { 1275 | "php": ">=5.3.3", 1276 | "sebastian/diff": "~1.2", 1277 | "sebastian/exporter": "~1.2 || ~2.0" 1278 | }, 1279 | "require-dev": { 1280 | "phpunit/phpunit": "~4.4" 1281 | }, 1282 | "type": "library", 1283 | "extra": { 1284 | "branch-alias": { 1285 | "dev-master": "1.2.x-dev" 1286 | } 1287 | }, 1288 | "autoload": { 1289 | "classmap": [ 1290 | "src/" 1291 | ] 1292 | }, 1293 | "notification-url": "https://packagist.org/downloads/", 1294 | "license": [ 1295 | "BSD-3-Clause" 1296 | ], 1297 | "authors": [ 1298 | { 1299 | "name": "Jeff Welch", 1300 | "email": "whatthejeff@gmail.com" 1301 | }, 1302 | { 1303 | "name": "Volker Dusch", 1304 | "email": "github@wallbash.com" 1305 | }, 1306 | { 1307 | "name": "Bernhard Schussek", 1308 | "email": "bschussek@2bepublished.at" 1309 | }, 1310 | { 1311 | "name": "Sebastian Bergmann", 1312 | "email": "sebastian@phpunit.de" 1313 | } 1314 | ], 1315 | "description": "Provides the functionality to compare PHP values for equality", 1316 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 1317 | "keywords": [ 1318 | "comparator", 1319 | "compare", 1320 | "equality" 1321 | ], 1322 | "time": "2017-01-29 09:50:25" 1323 | }, 1324 | { 1325 | "name": "sebastian/diff", 1326 | "version": "1.4.3", 1327 | "source": { 1328 | "type": "git", 1329 | "url": "https://github.com/sebastianbergmann/diff.git", 1330 | "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" 1331 | }, 1332 | "dist": { 1333 | "type": "zip", 1334 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", 1335 | "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", 1336 | "shasum": "" 1337 | }, 1338 | "require": { 1339 | "php": "^5.3.3 || ^7.0" 1340 | }, 1341 | "require-dev": { 1342 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" 1343 | }, 1344 | "type": "library", 1345 | "extra": { 1346 | "branch-alias": { 1347 | "dev-master": "1.4-dev" 1348 | } 1349 | }, 1350 | "autoload": { 1351 | "classmap": [ 1352 | "src/" 1353 | ] 1354 | }, 1355 | "notification-url": "https://packagist.org/downloads/", 1356 | "license": [ 1357 | "BSD-3-Clause" 1358 | ], 1359 | "authors": [ 1360 | { 1361 | "name": "Kore Nordmann", 1362 | "email": "mail@kore-nordmann.de" 1363 | }, 1364 | { 1365 | "name": "Sebastian Bergmann", 1366 | "email": "sebastian@phpunit.de" 1367 | } 1368 | ], 1369 | "description": "Diff implementation", 1370 | "homepage": "https://github.com/sebastianbergmann/diff", 1371 | "keywords": [ 1372 | "diff" 1373 | ], 1374 | "time": "2017-05-22 07:24:03" 1375 | }, 1376 | { 1377 | "name": "sebastian/environment", 1378 | "version": "2.0.0", 1379 | "source": { 1380 | "type": "git", 1381 | "url": "https://github.com/sebastianbergmann/environment.git", 1382 | "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" 1383 | }, 1384 | "dist": { 1385 | "type": "zip", 1386 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", 1387 | "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", 1388 | "shasum": "" 1389 | }, 1390 | "require": { 1391 | "php": "^5.6 || ^7.0" 1392 | }, 1393 | "require-dev": { 1394 | "phpunit/phpunit": "^5.0" 1395 | }, 1396 | "type": "library", 1397 | "extra": { 1398 | "branch-alias": { 1399 | "dev-master": "2.0.x-dev" 1400 | } 1401 | }, 1402 | "autoload": { 1403 | "classmap": [ 1404 | "src/" 1405 | ] 1406 | }, 1407 | "notification-url": "https://packagist.org/downloads/", 1408 | "license": [ 1409 | "BSD-3-Clause" 1410 | ], 1411 | "authors": [ 1412 | { 1413 | "name": "Sebastian Bergmann", 1414 | "email": "sebastian@phpunit.de" 1415 | } 1416 | ], 1417 | "description": "Provides functionality to handle HHVM/PHP environments", 1418 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1419 | "keywords": [ 1420 | "Xdebug", 1421 | "environment", 1422 | "hhvm" 1423 | ], 1424 | "time": "2016-11-26 07:53:53" 1425 | }, 1426 | { 1427 | "name": "sebastian/exporter", 1428 | "version": "2.0.0", 1429 | "source": { 1430 | "type": "git", 1431 | "url": "https://github.com/sebastianbergmann/exporter.git", 1432 | "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" 1433 | }, 1434 | "dist": { 1435 | "type": "zip", 1436 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", 1437 | "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", 1438 | "shasum": "" 1439 | }, 1440 | "require": { 1441 | "php": ">=5.3.3", 1442 | "sebastian/recursion-context": "~2.0" 1443 | }, 1444 | "require-dev": { 1445 | "ext-mbstring": "*", 1446 | "phpunit/phpunit": "~4.4" 1447 | }, 1448 | "type": "library", 1449 | "extra": { 1450 | "branch-alias": { 1451 | "dev-master": "2.0.x-dev" 1452 | } 1453 | }, 1454 | "autoload": { 1455 | "classmap": [ 1456 | "src/" 1457 | ] 1458 | }, 1459 | "notification-url": "https://packagist.org/downloads/", 1460 | "license": [ 1461 | "BSD-3-Clause" 1462 | ], 1463 | "authors": [ 1464 | { 1465 | "name": "Jeff Welch", 1466 | "email": "whatthejeff@gmail.com" 1467 | }, 1468 | { 1469 | "name": "Volker Dusch", 1470 | "email": "github@wallbash.com" 1471 | }, 1472 | { 1473 | "name": "Bernhard Schussek", 1474 | "email": "bschussek@2bepublished.at" 1475 | }, 1476 | { 1477 | "name": "Sebastian Bergmann", 1478 | "email": "sebastian@phpunit.de" 1479 | }, 1480 | { 1481 | "name": "Adam Harvey", 1482 | "email": "aharvey@php.net" 1483 | } 1484 | ], 1485 | "description": "Provides the functionality to export PHP variables for visualization", 1486 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1487 | "keywords": [ 1488 | "export", 1489 | "exporter" 1490 | ], 1491 | "time": "2016-11-19 08:54:04" 1492 | }, 1493 | { 1494 | "name": "sebastian/global-state", 1495 | "version": "1.1.1", 1496 | "source": { 1497 | "type": "git", 1498 | "url": "https://github.com/sebastianbergmann/global-state.git", 1499 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" 1500 | }, 1501 | "dist": { 1502 | "type": "zip", 1503 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", 1504 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", 1505 | "shasum": "" 1506 | }, 1507 | "require": { 1508 | "php": ">=5.3.3" 1509 | }, 1510 | "require-dev": { 1511 | "phpunit/phpunit": "~4.2" 1512 | }, 1513 | "suggest": { 1514 | "ext-uopz": "*" 1515 | }, 1516 | "type": "library", 1517 | "extra": { 1518 | "branch-alias": { 1519 | "dev-master": "1.0-dev" 1520 | } 1521 | }, 1522 | "autoload": { 1523 | "classmap": [ 1524 | "src/" 1525 | ] 1526 | }, 1527 | "notification-url": "https://packagist.org/downloads/", 1528 | "license": [ 1529 | "BSD-3-Clause" 1530 | ], 1531 | "authors": [ 1532 | { 1533 | "name": "Sebastian Bergmann", 1534 | "email": "sebastian@phpunit.de" 1535 | } 1536 | ], 1537 | "description": "Snapshotting of global state", 1538 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1539 | "keywords": [ 1540 | "global state" 1541 | ], 1542 | "time": "2015-10-12 03:26:01" 1543 | }, 1544 | { 1545 | "name": "sebastian/object-enumerator", 1546 | "version": "2.0.1", 1547 | "source": { 1548 | "type": "git", 1549 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1550 | "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" 1551 | }, 1552 | "dist": { 1553 | "type": "zip", 1554 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", 1555 | "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", 1556 | "shasum": "" 1557 | }, 1558 | "require": { 1559 | "php": ">=5.6", 1560 | "sebastian/recursion-context": "~2.0" 1561 | }, 1562 | "require-dev": { 1563 | "phpunit/phpunit": "~5" 1564 | }, 1565 | "type": "library", 1566 | "extra": { 1567 | "branch-alias": { 1568 | "dev-master": "2.0.x-dev" 1569 | } 1570 | }, 1571 | "autoload": { 1572 | "classmap": [ 1573 | "src/" 1574 | ] 1575 | }, 1576 | "notification-url": "https://packagist.org/downloads/", 1577 | "license": [ 1578 | "BSD-3-Clause" 1579 | ], 1580 | "authors": [ 1581 | { 1582 | "name": "Sebastian Bergmann", 1583 | "email": "sebastian@phpunit.de" 1584 | } 1585 | ], 1586 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1587 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1588 | "time": "2017-02-18 15:18:39" 1589 | }, 1590 | { 1591 | "name": "sebastian/recursion-context", 1592 | "version": "2.0.0", 1593 | "source": { 1594 | "type": "git", 1595 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1596 | "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" 1597 | }, 1598 | "dist": { 1599 | "type": "zip", 1600 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", 1601 | "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", 1602 | "shasum": "" 1603 | }, 1604 | "require": { 1605 | "php": ">=5.3.3" 1606 | }, 1607 | "require-dev": { 1608 | "phpunit/phpunit": "~4.4" 1609 | }, 1610 | "type": "library", 1611 | "extra": { 1612 | "branch-alias": { 1613 | "dev-master": "2.0.x-dev" 1614 | } 1615 | }, 1616 | "autoload": { 1617 | "classmap": [ 1618 | "src/" 1619 | ] 1620 | }, 1621 | "notification-url": "https://packagist.org/downloads/", 1622 | "license": [ 1623 | "BSD-3-Clause" 1624 | ], 1625 | "authors": [ 1626 | { 1627 | "name": "Jeff Welch", 1628 | "email": "whatthejeff@gmail.com" 1629 | }, 1630 | { 1631 | "name": "Sebastian Bergmann", 1632 | "email": "sebastian@phpunit.de" 1633 | }, 1634 | { 1635 | "name": "Adam Harvey", 1636 | "email": "aharvey@php.net" 1637 | } 1638 | ], 1639 | "description": "Provides functionality to recursively process PHP variables", 1640 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1641 | "time": "2016-11-19 07:33:16" 1642 | }, 1643 | { 1644 | "name": "sebastian/resource-operations", 1645 | "version": "1.0.0", 1646 | "source": { 1647 | "type": "git", 1648 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1649 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" 1650 | }, 1651 | "dist": { 1652 | "type": "zip", 1653 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", 1654 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", 1655 | "shasum": "" 1656 | }, 1657 | "require": { 1658 | "php": ">=5.6.0" 1659 | }, 1660 | "type": "library", 1661 | "extra": { 1662 | "branch-alias": { 1663 | "dev-master": "1.0.x-dev" 1664 | } 1665 | }, 1666 | "autoload": { 1667 | "classmap": [ 1668 | "src/" 1669 | ] 1670 | }, 1671 | "notification-url": "https://packagist.org/downloads/", 1672 | "license": [ 1673 | "BSD-3-Clause" 1674 | ], 1675 | "authors": [ 1676 | { 1677 | "name": "Sebastian Bergmann", 1678 | "email": "sebastian@phpunit.de" 1679 | } 1680 | ], 1681 | "description": "Provides a list of PHP built-in functions that operate on resources", 1682 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1683 | "time": "2015-07-28 20:34:47" 1684 | }, 1685 | { 1686 | "name": "sebastian/version", 1687 | "version": "2.0.1", 1688 | "source": { 1689 | "type": "git", 1690 | "url": "https://github.com/sebastianbergmann/version.git", 1691 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" 1692 | }, 1693 | "dist": { 1694 | "type": "zip", 1695 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", 1696 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", 1697 | "shasum": "" 1698 | }, 1699 | "require": { 1700 | "php": ">=5.6" 1701 | }, 1702 | "type": "library", 1703 | "extra": { 1704 | "branch-alias": { 1705 | "dev-master": "2.0.x-dev" 1706 | } 1707 | }, 1708 | "autoload": { 1709 | "classmap": [ 1710 | "src/" 1711 | ] 1712 | }, 1713 | "notification-url": "https://packagist.org/downloads/", 1714 | "license": [ 1715 | "BSD-3-Clause" 1716 | ], 1717 | "authors": [ 1718 | { 1719 | "name": "Sebastian Bergmann", 1720 | "email": "sebastian@phpunit.de", 1721 | "role": "lead" 1722 | } 1723 | ], 1724 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1725 | "homepage": "https://github.com/sebastianbergmann/version", 1726 | "time": "2016-10-03 07:35:21" 1727 | }, 1728 | { 1729 | "name": "symfony/polyfill-ctype", 1730 | "version": "v1.11.0", 1731 | "source": { 1732 | "type": "git", 1733 | "url": "https://github.com/symfony/polyfill-ctype.git", 1734 | "reference": "82ebae02209c21113908c229e9883c419720738a" 1735 | }, 1736 | "dist": { 1737 | "type": "zip", 1738 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", 1739 | "reference": "82ebae02209c21113908c229e9883c419720738a", 1740 | "shasum": "" 1741 | }, 1742 | "require": { 1743 | "php": ">=5.3.3" 1744 | }, 1745 | "suggest": { 1746 | "ext-ctype": "For best performance" 1747 | }, 1748 | "type": "library", 1749 | "extra": { 1750 | "branch-alias": { 1751 | "dev-master": "1.11-dev" 1752 | } 1753 | }, 1754 | "autoload": { 1755 | "psr-4": { 1756 | "Symfony\\Polyfill\\Ctype\\": "" 1757 | }, 1758 | "files": [ 1759 | "bootstrap.php" 1760 | ] 1761 | }, 1762 | "notification-url": "https://packagist.org/downloads/", 1763 | "license": [ 1764 | "MIT" 1765 | ], 1766 | "authors": [ 1767 | { 1768 | "name": "Symfony Community", 1769 | "homepage": "https://symfony.com/contributors" 1770 | }, 1771 | { 1772 | "name": "Gert de Pagter", 1773 | "email": "BackEndTea@gmail.com" 1774 | } 1775 | ], 1776 | "description": "Symfony polyfill for ctype functions", 1777 | "homepage": "https://symfony.com", 1778 | "keywords": [ 1779 | "compatibility", 1780 | "ctype", 1781 | "polyfill", 1782 | "portable" 1783 | ], 1784 | "time": "2019-02-06 07:57:58" 1785 | }, 1786 | { 1787 | "name": "symfony/yaml", 1788 | "version": "v4.3.3", 1789 | "source": { 1790 | "type": "git", 1791 | "url": "https://github.com/symfony/yaml.git", 1792 | "reference": "34d29c2acd1ad65688f58452fd48a46bd996d5a6" 1793 | }, 1794 | "dist": { 1795 | "type": "zip", 1796 | "url": "https://api.github.com/repos/symfony/yaml/zipball/34d29c2acd1ad65688f58452fd48a46bd996d5a6", 1797 | "reference": "34d29c2acd1ad65688f58452fd48a46bd996d5a6", 1798 | "shasum": "" 1799 | }, 1800 | "require": { 1801 | "php": "^7.1.3", 1802 | "symfony/polyfill-ctype": "~1.8" 1803 | }, 1804 | "conflict": { 1805 | "symfony/console": "<3.4" 1806 | }, 1807 | "require-dev": { 1808 | "symfony/console": "~3.4|~4.0" 1809 | }, 1810 | "suggest": { 1811 | "symfony/console": "For validating YAML files using the lint command" 1812 | }, 1813 | "type": "library", 1814 | "extra": { 1815 | "branch-alias": { 1816 | "dev-master": "4.3-dev" 1817 | } 1818 | }, 1819 | "autoload": { 1820 | "psr-4": { 1821 | "Symfony\\Component\\Yaml\\": "" 1822 | }, 1823 | "exclude-from-classmap": [ 1824 | "/Tests/" 1825 | ] 1826 | }, 1827 | "notification-url": "https://packagist.org/downloads/", 1828 | "license": [ 1829 | "MIT" 1830 | ], 1831 | "authors": [ 1832 | { 1833 | "name": "Fabien Potencier", 1834 | "email": "fabien@symfony.com" 1835 | }, 1836 | { 1837 | "name": "Symfony Community", 1838 | "homepage": "https://symfony.com/contributors" 1839 | } 1840 | ], 1841 | "description": "Symfony Yaml Component", 1842 | "homepage": "https://symfony.com", 1843 | "time": "2019-07-24 14:47:54" 1844 | }, 1845 | { 1846 | "name": "webmozart/assert", 1847 | "version": "1.4.0", 1848 | "source": { 1849 | "type": "git", 1850 | "url": "https://github.com/webmozart/assert.git", 1851 | "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" 1852 | }, 1853 | "dist": { 1854 | "type": "zip", 1855 | "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", 1856 | "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", 1857 | "shasum": "" 1858 | }, 1859 | "require": { 1860 | "php": "^5.3.3 || ^7.0", 1861 | "symfony/polyfill-ctype": "^1.8" 1862 | }, 1863 | "require-dev": { 1864 | "phpunit/phpunit": "^4.6", 1865 | "sebastian/version": "^1.0.1" 1866 | }, 1867 | "type": "library", 1868 | "extra": { 1869 | "branch-alias": { 1870 | "dev-master": "1.3-dev" 1871 | } 1872 | }, 1873 | "autoload": { 1874 | "psr-4": { 1875 | "Webmozart\\Assert\\": "src/" 1876 | } 1877 | }, 1878 | "notification-url": "https://packagist.org/downloads/", 1879 | "license": [ 1880 | "MIT" 1881 | ], 1882 | "authors": [ 1883 | { 1884 | "name": "Bernhard Schussek", 1885 | "email": "bschussek@gmail.com" 1886 | } 1887 | ], 1888 | "description": "Assertions to validate method input/output with nice error messages.", 1889 | "keywords": [ 1890 | "assert", 1891 | "check", 1892 | "validate" 1893 | ], 1894 | "time": "2018-12-25 11:19:39" 1895 | } 1896 | ], 1897 | "aliases": [], 1898 | "minimum-stability": "stable", 1899 | "stability-flags": [], 1900 | "prefer-stable": false, 1901 | "prefer-lowest": false, 1902 | "platform": { 1903 | "php": ">=5.6" 1904 | }, 1905 | "platform-dev": [] 1906 | } 1907 | -------------------------------------------------------------------------------- /examples/blake.php: -------------------------------------------------------------------------------- 1 | update($buffer); 26 | } 27 | 28 | // This function will append missing bytes for proper blake function 29 | // and will return 32 characters long hash string. 30 | $hash = $blakeState->finalize(); 31 | 32 | echo sprintf("BLAKE256: %s\n", $hash); 33 | -------------------------------------------------------------------------------- /examples/generate_address.php: -------------------------------------------------------------------------------- 1 | hardenedChildKey(44) 19 | ->hardenedChildKey(42) 20 | ->hardenedChildKey(0); 21 | 22 | // Default account HD public key 23 | $defaultAccountPublicKey = $master->neuter(); 24 | 25 | echo sprintf("Default account HD private key: %s\n", $defaultAccountPrivateKey); 26 | echo sprintf("Default account HD public key: %s\n", $defaultAccountPublicKey); 27 | 28 | // Default address 29 | $defaultAddress = $defaultAccountPublicKey 30 | ->publicChildKey(0) 31 | ->publicChildKey(0) 32 | ->getAddress(); 33 | 34 | echo sprintf("Default address: %s\n", $defaultAddress); 35 | -------------------------------------------------------------------------------- /examples/transaction_listen.php: -------------------------------------------------------------------------------- 1 | getDataClient(); 17 | 18 | while (!$stop) { 19 | 20 | $transactions = $client->getAddressRaw($address); 21 | 22 | echo "Transactions: \n"; 23 | var_export($transactions); 24 | sleep(1); 25 | } 26 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ./src 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Decred/Crypto/Blake/AbstractState.php: -------------------------------------------------------------------------------- 1 | padding, $start, $length))); 40 | } 41 | 42 | protected function u8to32(array $p) 43 | { 44 | return ((unpack('C', $p[0])[1] & 0xFFFFFFFF) << 24) | 45 | ((unpack('C', $p[1])[1] & 0xFFFFFFFF) << 16) | 46 | ((unpack('C', $p[2])[1] & 0xFFFFFFFF) << 8) | 47 | ((unpack('C', $p[3])[1] & 0xFFFFFFFF)); 48 | } 49 | 50 | protected function u32to8($u32) 51 | { 52 | return [ 53 | (($u32 >> 24) & 0xFF), 54 | (($u32 >> 16) & 0xFF), 55 | (($u32 >> 8) & 0xFF), 56 | (($u32) & 0xFF) 57 | ]; 58 | } 59 | 60 | protected function u32to8s($u32) 61 | { 62 | $p = $this->u32to8($u32); 63 | 64 | return "". 65 | sprintf("%02x", $p[0]). 66 | sprintf("%02x", $p[1]). 67 | sprintf("%02x", $p[2]). 68 | sprintf("%02x", $p[3]); 69 | } 70 | 71 | protected function g($v, $a, $b, $c, $d, $e, $m, $round) 72 | { 73 | $i = $round % 10; 74 | $j = $m[$this->sigma[$i][$e]] ^ $this->constant[$this->sigma[$i][$e + 1]]; 75 | $k = $m[$this->sigma[$i][$e + 1]] ^ $this->constant[$this->sigma[$i][$e]]; 76 | $v[$a] = $v[$a] + $v[$b] + $j; 77 | $v[$a] = (int) $v[$a] & 0xffffffff; 78 | $v[$d] = $this->rotate($v[$d] ^ $v[$a], 16); 79 | $v[$d] = (int) $v[$d] & 0xffffffff; 80 | $v[$c] = $v[$c] + $v[$d]; 81 | $v[$c] = (int) $v[$c] & 0xffffffff; 82 | $v[$b] = $this->rotate($v[$b] ^ $v[$c], 12); 83 | $v[$b] = (int) $v[$b] & 0xffffffff; 84 | $v[$a] = $v[$a] + $v[$b] + $k; 85 | $v[$a] = (int) $v[$a] & 0xffffffff; 86 | $v[$d] = $this->rotate($v[$d] ^ $v[$a], 8); 87 | $v[$d] = (int) $v[$d] & 0xffffffff; 88 | $v[$c] = $v[$c] + $v[$d]; 89 | $v[$c] = (int) $v[$c] & 0xffffffff; 90 | $v[$b] = $this->rotate($v[$b] ^ $v[$c], 7); 91 | $v[$b] = (int) $v[$b] & 0xffffffff; 92 | return $v; 93 | } 94 | 95 | private function rotate($x, $n) 96 | { 97 | return (($x << (32 - $n)) & 0xFFFFFFFF) | ((($x) >> ($n) & 0xFFFFFFFF)); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Decred/Crypto/Blake/README.md: -------------------------------------------------------------------------------- 1 | # Blake 256 PHP Native implementation 2 | 3 | Implementation of BLAKE256 hash algorithm in native PHP. 4 | 5 | ## Usage 6 | 7 | ```php 8 | $blakeState = new \Decred\Crypto\Blake\Blake256(); 9 | 10 | $handler = fopen($file, 'r'); 11 | 12 | while (!feof($handler)) { 13 | 14 | $buffer = fread($handler, 64); 15 | 16 | $blakeState->update($buffer); 17 | } 18 | 19 | $hash = $blakeState->finalize(); 20 | 21 | ``` 22 | -------------------------------------------------------------------------------- /src/Decred/Crypto/Blake/State256.php: -------------------------------------------------------------------------------- 1 | s = is_array($s) && count($s) === 4 ? $s : [0, 0, 0, 0]; 31 | $this->nullt = 0; 32 | $this->buffer = ""; 33 | $this->h = [ 34 | 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 35 | 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 36 | ]; 37 | } 38 | 39 | /** 40 | * @param string $data 41 | */ 42 | public function update($data) 43 | { 44 | $left = strlen($this->buffer); 45 | $fill = 64 - $left; 46 | 47 | if ($left && (strlen($data) >= $fill)) { 48 | $this->t[0] = $this->t[0] + 512; 49 | $this->t[0] = $this->t[0] & 0xFFFFFFFF; 50 | if ($this->t[0] == 0) { 51 | $this->t[1] = $this->t[1] + 1; 52 | } 53 | 54 | $this->compress($this->buffer.substr($data, 0, $fill)); 55 | 56 | $data = substr($data, $fill); 57 | $this->buffer = ""; 58 | } 59 | 60 | while (strlen($data) >= 64) { 61 | $this->t[0] = $this->t[0] + 512; 62 | if ($this->t[0] == 0) { 63 | $this->t[1] = $this->t[1] + 1; 64 | } 65 | 66 | $this->compress(substr($data, 0, 64)); 67 | $data = substr($data, 64); 68 | } 69 | 70 | if (strlen($data) > 0) { 71 | $this->buffer = $this->buffer.$data; 72 | } else { 73 | $this->buffer = ""; 74 | } 75 | } 76 | 77 | /** 78 | * @return string 79 | */ 80 | public function finalize() 81 | { 82 | $buflen = strlen($this->buffer); 83 | $lo = $this->t[0] + ($buflen << 3); 84 | $hi = $this->t[1]; 85 | $zo = "\x01"; 86 | $oo = "\x81"; 87 | 88 | if ($lo < ($buflen << 3)) { 89 | $hi += 1; 90 | } 91 | 92 | $hi8 = $this->u32to8($hi); 93 | $lo8 = $this->u32to8($lo); 94 | $msglen = implode("", array_map("chr", [ 95 | $hi8[0], $hi8[1], $hi8[2], $hi8[3], 96 | $lo8[0], $lo8[1], $lo8[2], $lo8[3]] 97 | )); 98 | 99 | if ($buflen === 55) { 100 | 101 | $this->update($oo); 102 | $this->t[0] = $this->t[0] + 8; 103 | 104 | } else { 105 | if ($buflen < 55) { 106 | if (!$buflen) { 107 | $this->nullt = 1; 108 | } 109 | 110 | $this->t[0] = $this->t[0] - (440 - ($buflen << 3)); 111 | $this->update($this->padding(0, 55 - $buflen)); 112 | 113 | } else { 114 | 115 | $this->t[0] = $this->t[0] - (512 - ($buflen << 3)); 116 | $this->update($this->padding(0, 64 - $buflen)); 117 | $this->t[0] = $this->t[0] - 440; 118 | $this->update($this->padding(1, 55)); 119 | $this->nullt = 1; 120 | 121 | } 122 | 123 | $this->update($zo); 124 | $this->t[0] = $this->t[0] - 8; 125 | } 126 | 127 | $this->t[0] = $this->t[0] - 64; 128 | $this->update($msglen); 129 | 130 | $out = ""; 131 | for ($i = 0; $i < 8; $i++) { 132 | $out = $out.$this->u32to8s($this->h[$i]); 133 | } 134 | 135 | return $out; 136 | } 137 | 138 | /** 139 | * @param string $data 140 | */ 141 | private function compress($data) 142 | { 143 | $m = []; 144 | 145 | for ($i = 0; $i < 16; $i++) { 146 | $m1 = $data[$i * 4]; 147 | $m2 = $data[($i * 4) + 1]; 148 | $m3 = $data[($i * 4) + 2]; 149 | $m4 = $data[($i * 4) + 3]; 150 | $m[$i] = $this->u8to32([$m1, $m2, $m3, $m4]); 151 | } 152 | 153 | $v = [ 154 | $this->h[0], $this->h[1], $this->h[2], $this->h[3], 155 | $this->h[4], $this->h[5], $this->h[6], $this->h[7], 156 | $this->s[0] ^ $this->constant[0], 157 | $this->s[1] ^ $this->constant[1], 158 | $this->s[2] ^ $this->constant[2], 159 | $this->s[3] ^ $this->constant[3], 160 | $this->constant[4], 161 | $this->constant[5], 162 | $this->constant[6], 163 | $this->constant[7] 164 | ]; 165 | 166 | if (!$this->nullt) { 167 | $v[12] = $v[12] ^ $this->t[0]; 168 | $v[13] = $v[13] ^ $this->t[0]; 169 | $v[14] = $v[14] ^ $this->t[1]; 170 | $v[15] = $v[15] ^ $this->t[1]; 171 | } 172 | 173 | for ($i = 0; $i < 14; $i++) { 174 | $v = $this->g($v, 0, 4, 8, 12, 0, $m, $i); 175 | $v = $this->g($v, 1, 5, 9, 13, 2, $m, $i); 176 | $v = $this->g($v, 2, 6, 10, 14, 4, $m, $i); 177 | $v = $this->g($v, 3, 7, 11, 15, 6, $m, $i); 178 | 179 | $v = $this->g($v, 0, 5, 10, 15, 8, $m, $i); 180 | $v = $this->g($v, 1, 6, 11, 12, 10, $m, $i); 181 | $v = $this->g($v, 2, 7, 8, 13, 12, $m, $i); 182 | $v = $this->g($v, 3, 4, 9, 14, 14, $m, $i); 183 | } 184 | 185 | for ($i = 0; $i < 16; $i++) { 186 | $ix = $i % 8; 187 | $this->h[$ix] = $this->h[$ix] ^ $v[$i]; 188 | } 189 | 190 | for ($i = 0; $i < 8; $i++) { 191 | $ix = $i % 4; 192 | $this->h[$ix] = $this->h[$ix] ^ $this->s[$ix]; 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/Decred/Crypto/Curve.php: -------------------------------------------------------------------------------- 1 | curve256k1(); 33 | } 34 | 35 | return static::$curve; 36 | } 37 | 38 | /** 39 | * @return GeneratorPoint 40 | */ 41 | public static function generator() 42 | { 43 | if (!static::$generator) { 44 | static::$generator = EccFactory::getSecgCurves()->generator256k1(); 45 | } 46 | 47 | return static::$generator; 48 | } 49 | 50 | /** 51 | * @return CompressedPointSerializer 52 | */ 53 | public static function serializer() 54 | { 55 | if (!static::$serializer) { 56 | static::$serializer = new CompressedPointSerializer(static::generator()->getAdapter()); 57 | } 58 | 59 | return static::$serializer; 60 | } 61 | 62 | /** 63 | * @param PointInterface $point 64 | * 65 | * @return string 66 | */ 67 | public static function serializePoint(PointInterface $point) 68 | { 69 | return hex2bin(static::serializer()->serialize($point)); 70 | } 71 | 72 | /** 73 | * @param string $data 74 | * 75 | * @return PointInterface 76 | */ 77 | public static function unserializePoint($data) 78 | { 79 | return static::serializer()->unserialize(static::curve(), bin2hex($data)); 80 | } 81 | 82 | /** 83 | * @param \GMP $p 84 | * 85 | * @throws \RuntimeException 86 | */ 87 | public static function ensureUsableKey($p) 88 | { 89 | if (((gmp_cmp($p, 0)) == 0) || (gmp_cmp($p, Curve::generator()->getOrder()) >= 0)) { 90 | throw new \RuntimeException('The extended key at this index is invalid'); 91 | } 92 | } 93 | 94 | /** 95 | * Is key on the secp256k1 curve. 96 | * 97 | * @param string $key 98 | * 99 | * @return bool 100 | */ 101 | public static function ensureOnCurve($key) 102 | { 103 | $point = self::unserializePoint($key); 104 | if (!static::curve()->contains($point->getX(), $point->getY())) { 105 | throw new \InvalidArgumentException( 106 | 'Invalid key. Point is not on the curve.' 107 | ); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Decred/Crypto/DecredNetwork.php: -------------------------------------------------------------------------------- 1 | decode($key); 33 | 34 | if (strlen($decoded) !== 82) { 35 | throw new \InvalidArgumentException('Wrong key length.'); 36 | } 37 | 38 | // Split the payload and checksum up and ensure the checksum matches. 39 | $payload = substr($decoded, 0, 78); 40 | $expected = substr($decoded, -4); 41 | 42 | // Verify checksum of provided key 43 | $network = NetworkFactory::fromExtendedKeyVersion(substr($payload, 0, 4)); 44 | $checksum = substr($network->base58Checksum($payload), 0, 4); 45 | 46 | if ($expected !== $checksum) { 47 | throw new \InvalidArgumentException('Wrong checksum on encoding extended key!'); 48 | } 49 | 50 | return $decoded; 51 | } 52 | 53 | /** 54 | * @inheritdoc 55 | */ 56 | public function base58EncodeAddress($key) 57 | { 58 | $prefix = $this->HDPubKeyHashAddrId(); 59 | $payload = $prefix.hash('ripemd160', $this->hashKey256($key), true); 60 | return $this->base58()->encode($payload.$this->base58Checksum($payload)); 61 | } 62 | 63 | /** 64 | * @inheritdoc 65 | */ 66 | public function hashKey256($key) 67 | { 68 | return hex2bin(Hash::blake($key)); 69 | } 70 | 71 | /** 72 | * @inheritdoc 73 | */ 74 | public function base58EncodeChecksum($payload) 75 | { 76 | return $this->base58()->encode($payload.$this->base58Checksum($payload)); 77 | } 78 | 79 | /** 80 | * @inheritdoc 81 | */ 82 | public function base58Checksum($payload) 83 | { 84 | return substr($this->hashKey256($this->hashKey256($payload)), 0, 4); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Decred/Crypto/ExtendedKey.php: -------------------------------------------------------------------------------- 1 | static::MAX_SEED_BYTES)) { 120 | throw new \InvalidArgumentException( 121 | 'Invalid seed length. Length should be between 16 and 64 bytes (32 recommended).' 122 | ); 123 | } 124 | 125 | $seed = random_bytes($length); 126 | 127 | while (!static::verifySeed($seed, $network)) { 128 | // @codeCoverageIgnoreStart 129 | $seed = random_bytes($length); 130 | // @codeCoverageIgnoreEnd 131 | } 132 | 133 | return $seed; 134 | } 135 | 136 | /** 137 | * Verify that we can derive external branch 0 index address to check if seed is usable. 138 | * 139 | * @param string $seed 140 | * @param NetworkInterface $network 141 | * 142 | * @return ExtendedKey|bool Master key or false 143 | */ 144 | public static function verifySeed($seed, $network) 145 | { 146 | try { 147 | $master = static::newMaster($seed, $network); 148 | $coinType = $master->deriveCoinTypeKey(); 149 | $account0 = $coinType->deriveAccountKey(); 150 | $account0->neuter(); 151 | $account0->deriveInternalBranch(); 152 | $externalBranch = $account0->deriveExternalBranch(); 153 | $externalBranch0 = $externalBranch->privateChildKey(0); 154 | $externalBranch0->getAddress(); 155 | return $master; 156 | // @codeCoverageIgnoreStart 157 | } catch (\Exception $e) { 158 | return false; 159 | } 160 | // @codeCoverageIgnoreEnd 161 | } 162 | 163 | /** 164 | * @param string $seed 165 | * @param NetworkInterface $network 166 | * 167 | * @return static 168 | */ 169 | public static function newMaster($seed, NetworkInterface $network) 170 | { 171 | // Per [BIP32], the seed must be in range [16, 64]. 172 | if ((strlen($seed) < static::MIN_SEED_BYTES) || (strlen($seed) > static::MAX_SEED_BYTES)) { 173 | throw new \InvalidArgumentException( 174 | 'Invalid seed length. Length should be between 16 and 64 bytes (32 recommended).' 175 | ); 176 | } 177 | 178 | $I = hash_hmac('sha512', $seed, static::MASTER_KEY, true); 179 | $IL = substr($I, 0, (strlen($I) / 2)); 180 | $IR = substr($I, -(strlen($I) / 2)); 181 | 182 | $secretKey = str_pad(gmp_export(gmp_import($IL)), 32, "\x0", STR_PAD_LEFT); 183 | 184 | Curve::ensureUsableKey(gmp_import($secretKey)); 185 | 186 | return new ExtendedKey($secretKey, $IR, 0, "\x00\x00\x00\x00", 0, $network, true); 187 | } 188 | 189 | /** 190 | * Decode from base58 encoded string. 191 | * 192 | * @param string $key 193 | * 194 | * @return ExtendedKey 195 | */ 196 | public static function fromString($key) 197 | { 198 | // version (4) || depth (1) || parent fingerprint (4) || 199 | // child num (4) || chain code (32) || key data (33) || checksum (4) 200 | $payload = DecredNetwork::extendedKeyBase58Decode($key); 201 | $network = NetworkFactory::fromExtendedKeyVersion(substr($payload, 0, 4)); 202 | $depth = intval(hexdec(substr($payload, 4, 1))); 203 | $parentFP = substr($payload, 5, 4); 204 | $childNum = intval(hexdec(substr($payload, 9, 4))); 205 | $chainCode = substr($payload, 13, 32); 206 | $key = substr($payload, 45, 33); 207 | $private = $payload[45] === "\x00"; 208 | 209 | return new ExtendedKey($key, $chainCode, $depth, $parentFP, $childNum, $network, $private); 210 | } 211 | 212 | /** 213 | * Bip32ExtendedKey constructor. 214 | * 215 | * @param string $key 216 | * @param string $chainCode 217 | * @param int $depth 218 | * @param string $parentFP 219 | * @param int $childNum 220 | * @param NetworkInterface $network 221 | * @param bool $isPrivate 222 | */ 223 | public function __construct($key, $chainCode, $depth, $parentFP, $childNum, $network, $isPrivate = false) 224 | { 225 | if ($isPrivate) { 226 | Curve::ensureUsableKey(gmp_import($key)); 227 | } 228 | 229 | if (!$isPrivate) { 230 | Curve::ensureOnCurve($key); 231 | } 232 | 233 | if ($depth < 0 || $depth > ((1 << 8) - 1)) { 234 | throw new \InvalidArgumentException( 235 | 'Invalid depth for BIP32 key, must be in range [0 - 255] inclusive' 236 | ); 237 | } 238 | 239 | if (gmp_import($parentFP) < 0 || gmp_import($parentFP) > ((1 << 32) - 1)) { 240 | throw new \InvalidArgumentException( 241 | 'Invalid fingerprint for BIP32 key, must be in range [0 - (2^31)-1] inclusive' 242 | ); 243 | } 244 | 245 | if ($childNum < 0 || $childNum > ((1 << 32) - 1)) { 246 | throw new \InvalidArgumentException( 247 | 'Invalid childnum for BIP32 key, must be in range [0 - (2^31)-1] inclusive' 248 | ); 249 | } 250 | 251 | if (strlen($chainCode) !== 32) { 252 | throw new \RuntimeException('Chain code should be 32 bytes'); 253 | } 254 | 255 | $this->key = $isPrivate ? $key : null; 256 | $this->publicKey = $isPrivate ? null : $key; 257 | $this->chainCode = $chainCode; 258 | $this->depth = (int) $depth; 259 | $this->parentFP = $parentFP; 260 | $this->childNum = (int) $childNum; 261 | $this->network = $network; 262 | $this->isPrivate = (bool) $isPrivate; 263 | } 264 | 265 | /** 266 | * @return NetworkInterface 267 | */ 268 | public function getNetwork() 269 | { 270 | return $this->network; 271 | } 272 | 273 | /** 274 | * @return bool 275 | */ 276 | public function isPrivate() 277 | { 278 | return $this->isPrivate; 279 | } 280 | 281 | /** 282 | * @return bool 283 | */ 284 | public function isPublic() 285 | { 286 | return !$this->isPrivate; 287 | } 288 | 289 | /** 290 | * @return string 291 | */ 292 | public function privateKey() 293 | { 294 | if ($this->isPublic() || !$this->key) { 295 | throw new \LogicException('Cannot derive a extended private key from a extended public key'); 296 | } 297 | 298 | return $this->key; 299 | } 300 | 301 | /** 302 | * @return string 303 | */ 304 | public function publicKey() 305 | { 306 | if ($this->publicKey === null) { 307 | // Get public key from private key: serP(point(privateKey)) 308 | $this->publicKey = Curve::serializePoint( 309 | Curve::generator() 310 | ->getPrivateKeyFrom(gmp_import($this->key)) 311 | ->getPublicKey() 312 | ->getPoint() 313 | ); 314 | } 315 | 316 | return $this->publicKey; 317 | } 318 | 319 | /** 320 | * @return int 321 | */ 322 | public function depth() 323 | { 324 | return $this->depth; 325 | } 326 | 327 | /** 328 | * @return string 329 | */ 330 | public function chainCode() 331 | { 332 | return $this->chainCode; 333 | } 334 | 335 | /** 336 | * @return int|string 337 | */ 338 | public function parentFP() 339 | { 340 | return $this->parentFP; 341 | } 342 | 343 | /** 344 | * @return int 345 | */ 346 | public function childNum() 347 | { 348 | return $this->childNum; 349 | } 350 | 351 | /** 352 | * @param $index 353 | * 354 | * @return ExtendedKey 355 | */ 356 | public function hardenedChildKey($index) 357 | { 358 | return $this->privateChildKey(self::HARDENED_KEY_START + $index); 359 | } 360 | 361 | /** 362 | * @param int|\GMP $index 363 | * 364 | * @return ExtendedKey 365 | */ 366 | public function privateChildKey($index) 367 | { 368 | $key = $this->privateKey(); 369 | 370 | if ($index >= self::HARDENED_KEY_START) { 371 | // Data = 0x00 || ser256(kpar) || ser32(i)). 372 | // The 0x00 pads the private key to make it 33 bytes long. 373 | $data = "\x00".str_pad(gmp_export(gmp_import($key)), 32, "\x0", STR_PAD_LEFT).pack('N', $index); 374 | } else { 375 | $data = $this->publicKey().pack('N', $index); 376 | } 377 | 378 | $I = hash_hmac('sha512', $data, $this->chainCode, true); 379 | $IL = substr($I, 0, (strlen($I) / 2)); 380 | $IR = substr($I, -(strlen($I) / 2)); 381 | 382 | Curve::ensureUsableKey(gmp_import($IL)); 383 | 384 | $childKey = gmp_export(gmp_mod(gmp_add(gmp_import($IL), gmp_import($key)), Curve::generator()->getOrder())); 385 | 386 | $parentFP = substr(hex2bin(hash('ripemd160', $this->network->hashKey256($this->publicKey()))), 0, 4); 387 | 388 | return new ExtendedKey($childKey, $IR, $this->depth + 1, $parentFP, $index, $this->network, true); 389 | } 390 | 391 | /** 392 | * @param int|\GMP $index 393 | * 394 | * @return ExtendedKey 395 | */ 396 | public function publicChildKey($index) 397 | { 398 | if ($this->isPublic() && $index >= self::HARDENED_KEY_START) { 399 | throw new \LogicException('Cannot derive a hardened key from a public key'); 400 | } 401 | 402 | $key = $this->publicKey(); 403 | 404 | $data = $key.pack('N', $index); 405 | 406 | $I = hash_hmac('sha512', $data, $this->chainCode, true); 407 | $IL = substr($I, 0, (strlen($I) / 2)); 408 | $IR = substr($I, -(strlen($I) / 2)); 409 | 410 | Curve::ensureUsableKey(gmp_import($IL)); 411 | 412 | // The algorithm used to derive the public child key is: 413 | // childKey = serP(point(parse256(Il)) + parentKey) 414 | $childKey = Curve::serializePoint( 415 | Curve::generator() 416 | ->getPrivateKeyFrom(gmp_import($IL)) 417 | ->getPublicKey() 418 | ->getPoint() 419 | ->add(Curve::unserializePoint($key)) 420 | ); 421 | 422 | $parentFP = substr(hex2bin(hash('ripemd160', $this->network->hashKey256($key))), 0, 4); 423 | 424 | return new ExtendedKey($childKey, $IR, $this->depth + 1, $parentFP, $index, $this->network); 425 | } 426 | 427 | /** 428 | * Get public extended key from private or verify it is public key.. 429 | * 430 | * @return ExtendedKey 431 | */ 432 | public function neuter() 433 | { 434 | if ($this->isPrivate()) { 435 | return new ExtendedKey( 436 | $this->publicKey(), 437 | $this->chainCode, 438 | $this->depth, 439 | $this->parentFP, 440 | $this->childNum, 441 | $this->network 442 | ); 443 | } 444 | 445 | return $this; 446 | } 447 | 448 | /** 449 | * @return string 450 | */ 451 | public function getAddress() 452 | { 453 | return $this->network->base58EncodeAddress($this->publicKey()); 454 | } 455 | 456 | /** 457 | * @return string 458 | */ 459 | public function __toString() 460 | { 461 | $payload = $this->network->HDVersion($this->isPrivate); 462 | $payload .= chr($this->depth); 463 | $payload .= $this->parentFP; 464 | $payload .= pack('N', $this->childNum); 465 | $payload .= $this->chainCode; 466 | $payload .= $this->isPrivate() ? "\x00".$this->key : $this->publicKey; 467 | 468 | return $this->network->base58EncodeChecksum($payload); 469 | } 470 | 471 | /** 472 | * Derive BIP44 purpose and coin type. 473 | * 474 | * @param int $coinType 475 | * 476 | * @return ExtendedKey 477 | */ 478 | public function deriveCoinTypeKey($coinType = self::DECRED_COIN_TYPE) 479 | { 480 | if ($coinType > self::MAX_COIN_TYPE) { 481 | throw new \InvalidArgumentException('Invalid coin type.'); 482 | } 483 | 484 | return $this->hardenedChildKey(static::BIP44_PURPOSE)->hardenedChildKey($coinType); 485 | } 486 | 487 | /** 488 | * Derive account key after deriving purpose and coin type. 489 | * 490 | * @param int $account 491 | * 492 | * @return ExtendedKey 493 | */ 494 | public function deriveAccountKey($account = self::DEFAULT_ACCOUNT) 495 | { 496 | return $this->hardenedChildKey($account); 497 | } 498 | 499 | /** 500 | * @return ExtendedKey 501 | */ 502 | public function deriveExternalBranch() 503 | { 504 | return $this->privateChildKey(static::EXTERNAL_BRANCH); 505 | } 506 | 507 | /** 508 | * @return ExtendedKey 509 | */ 510 | public function deriveInternalBranch() 511 | { 512 | return $this->privateChildKey(static::INTERNAL_BRANCH); 513 | } 514 | } 515 | -------------------------------------------------------------------------------- /src/Decred/Crypto/Hash.php: -------------------------------------------------------------------------------- 1 | update($data); 14 | return $state->finalize(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Decred/Crypto/NetworkFactory.php: -------------------------------------------------------------------------------- 1 | guzzle = new Client([ 21 | 'base_uri' => $url, 22 | 'verify' => false, 23 | 'allow_redirects' => false, 24 | ]); 25 | } 26 | 27 | /** 28 | * @param Client $guzzle 29 | * 30 | * @return $this 31 | */ 32 | public function setGuzzle(Client $guzzle) 33 | { 34 | $this->guzzle = $guzzle; 35 | return $this; 36 | } 37 | 38 | /** 39 | * Get address raw presentation. 40 | * 41 | * @param string $address Address 42 | * @param \DateTime|null $from Filter older transactions 43 | * 44 | * @return array|bool|Transaction[] 45 | */ 46 | public function getAddressRaw($address, \DateTime $from = null) 47 | { 48 | $result = false; 49 | 50 | $response = $this->request(sprintf('/api/address/%s/raw', $address)); 51 | 52 | if ($response !== false && is_array($response)) { 53 | $result = []; 54 | foreach ($response as $data) { 55 | $transaction = new Transaction($data); 56 | 57 | if ($transaction->getOutAmount($address) !== false) { 58 | if ($from === null || $transaction->getTime() > $from) { 59 | $result[] = $transaction; 60 | } 61 | } 62 | } 63 | } 64 | 65 | return $result; 66 | } 67 | 68 | /** 69 | * @param string$path 70 | * 71 | * @return array|false 72 | */ 73 | protected function request($path) 74 | { 75 | try { 76 | return @json_decode($this->guzzle->get($path)->getBody(), true); 77 | } catch (\Exception $e) { 78 | return false; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Decred/Data/Transaction.php: -------------------------------------------------------------------------------- 1 | data = $data; 22 | } 23 | 24 | /** 25 | * @param string $forAddress 26 | * 27 | * @return float|bool Return amount or FALSE on failure. 28 | */ 29 | public function getOutAmount($forAddress) 30 | { 31 | if (isset($this->data['vout']) && is_array($this->data['vout'])) { 32 | foreach ($this->data['vout'] as $vout) { 33 | 34 | if (isset($vout['scriptPubKey']['addresses']) && is_array($vout['scriptPubKey']['addresses'])) { 35 | foreach ($vout['scriptPubKey']['addresses'] as $address) { 36 | 37 | if ($forAddress === $address && isset($vout['value'])) { 38 | return (float) $vout['value']; 39 | } 40 | } 41 | } 42 | 43 | } 44 | } 45 | 46 | return false; 47 | } 48 | 49 | /** 50 | * @return false|string 51 | */ 52 | public function getTxid() 53 | { 54 | return $this->data['txid']; 55 | } 56 | 57 | /** 58 | * @return \DateTime 59 | */ 60 | public function getTime() 61 | { 62 | $time = new \DateTime(); 63 | $time->setTimestamp($this->data['time']); 64 | return $time; 65 | } 66 | 67 | /** 68 | * @return int 69 | */ 70 | public function getConfirmations() 71 | { 72 | if (isset($this->data['confirmations'])) { 73 | return intval($this->data['confirmations']); 74 | } 75 | 76 | return 0; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Decred/MainNet.php: -------------------------------------------------------------------------------- 1 | provider = $provider; 31 | $this->currency = $currency; 32 | } 33 | 34 | /** 35 | * @return RateProviderInterface 36 | */ 37 | public function getProvider() 38 | { 39 | return $this->provider; 40 | } 41 | 42 | /** 43 | * @return string 44 | */ 45 | public function getCurrency() 46 | { 47 | return $this->currency; 48 | } 49 | 50 | /** 51 | * @return float 52 | */ 53 | public function getPrice() 54 | { 55 | if ($this->price === null) { 56 | $this->price = round((float) $this->provider->getPrice($this->currency), 4); 57 | } 58 | 59 | return $this->price; 60 | } 61 | 62 | /** 63 | * @return float 64 | */ 65 | public function getToRate() 66 | { 67 | return round(1 / $this->getPrice(), 8); 68 | } 69 | 70 | /** 71 | * @return float 72 | */ 73 | public function getFromRate() 74 | { 75 | return round($this->getPrice(), 8); 76 | } 77 | 78 | /** 79 | * @param mixed $amount 80 | * 81 | * @return string 82 | */ 83 | public function convertToCrypto($amount) 84 | { 85 | return round($amount / $this->getPrice(), 8); 86 | } 87 | 88 | /** 89 | * @param $amount 90 | * 91 | * @return string 92 | */ 93 | public function convertToFiat($amount) 94 | { 95 | return round($amount * $this->getPrice(), 4); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Decred/Rate/CoinMarketCap.php: -------------------------------------------------------------------------------- 1 | client = new Client([ 25 | 'base_uri' => 'https://api.coinmarketcap.com/v1/', 26 | 'timeout' => 10, 27 | ]); 28 | } 29 | 30 | /** 31 | * @param Client $client 32 | * 33 | * @return $this 34 | */ 35 | public function setClient(Client $client) 36 | { 37 | $this->client = $client; 38 | return $this; 39 | } 40 | 41 | /** 42 | * @param string $currency 43 | * 44 | * @return Rate 45 | */ 46 | public static function getRate($currency = self::DEFAULT_CURRENCY) 47 | { 48 | return new Rate(new static(), $currency); 49 | } 50 | 51 | /** 52 | * @param string $currency 53 | * 54 | * @return string 55 | */ 56 | public function getPrice($currency = self::DEFAULT_CURRENCY) 57 | { 58 | $priceKey = 'price_'.strtolower($currency); 59 | $response = $this->request(sprintf('ticker/%s/?convert=%s', static::DECRED_TICKER, $currency)); 60 | 61 | if (!isset($response[$priceKey])) { 62 | throw new \RuntimeException(sprintf('Missing currency rate %s!', $currency)); 63 | } 64 | 65 | return $response[$priceKey]; 66 | } 67 | 68 | /** 69 | * @param string $url 70 | * 71 | * @return array 72 | */ 73 | protected function request($url) 74 | { 75 | $response = $this->client->request('GET', $url); 76 | 77 | if ($response->getStatusCode() !== 200) { 78 | throw new \RuntimeException('Fetching rates request failed!'); 79 | } 80 | 81 | $json = @json_decode($response->getBody(), true); 82 | 83 | if (!$json || !is_array($json) || !isset($json[0])) { 84 | throw new \LogicException('Get wrong response on rates requests!'); 85 | } 86 | 87 | return $json[0]; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Decred/Rate/RateProviderInterface.php: -------------------------------------------------------------------------------- 1 | assertEquals('716f6e863f744b9ac22c97ec7b76ea5f5908bc5b2f67c61510bfc4751384ea7a', blake256('')); 14 | $this->assertEquals('0a332a295575cc2170894b4fd606f2ef145a292de41724a61a5c91614f0e7bd0', blake256("\x60")); 15 | $this->assertEquals('ae873b56e3dfe3ab133bfd1f7cace238a7398994c882044b57d0896d123f2ebe', blake256("\xfd\xa2")); 16 | $this->assertEquals('7146f02e6adea9c634b0cf365df235585c41bbbbfd2acb0233fd7ebe80e13a12', blake256("\x44\x17\x66\x9f")); 17 | $this->assertEquals('55fff13a73af487c043be583a00e9debd542d403136b5cd5c086a356885f538d', blake256("\x11\x3e\x7f\x90\xa7\x2c\x73\x39")); 18 | $this->assertEquals('7e35cfaa6c3882d7790b06c17785adfd13c2d7d34ad60a182febcc5a5234cff1', blake256("\x0e\xee\x7b\x86\xce\x55\x54\xfa\x15\x88\xba\x3f\x58\xd7\x0a\x60")); 19 | $this->assertEquals('6bfbb875f182a2f9ce65c54eabebab4804a3fd5ab2b0649c5d9376b32361c25a', blake256("\xd5\xcb\xa3\x5a\x1c\x6c\x05\x05\x63\xc2\x51\x7a\xdf\xe1\x35\x2f\x76\x61\xb6\x56\xe8\x86\x1b\x09\x3b\xc7\x73\x29\xc8\xab\x42\x19")); 20 | $this->assertEquals('e299dc492fc9d50e76afe7207bd1a84eeaa2722a3493def8e8180b92bdb8c9f3', blake256("\x02\x27\x98\xfd\x4d\x32\x72\xed\x79\xa1\x29\x7e\x5d\xb6\x45\xc9\xec\xb9\x82\x6e\x1b\x4b\x91\x8d\xf9\x4c\xce\xf9\x32\xf4\x0f\x0d\xf1\x14\x78\x80\x41\x9c\x52\x5b\xd2\x4d\x6f\x35\xf4\x79\xcf\xc8\x80\x62\x68\x27\x7d\x18\x5a\xaf\xab\x7f\xf5\x2d\xf0\x77\xb2\x92")); 21 | $this->assertEquals('d3ac742166af246be73e98500e9b707a83971d4761d9f348093dbcbd44986305', blake256("\x29\x4a\xea\xcc\x73\x24\x48\x2f\xf5\x43\x6b\x5e\x53\x87\x95\x13\xc6\xe5\xa8\x91\xf3\xf9\x49\xcb\x35\xf0\x8b\x14\x73\xef\xdd\x7a\x0e\xf2\x6f\x39\x6b\xb2\x01\xa2\x8d\x86\x2e\xda\x8f\xb6\x92\x05\xcd\x67\xf4\xc7\xc9\x8c\x5e\x76\xb1\x3e\xa4\xa6\xec\x96\xfc\xe8\xd7\x60\xa1\x5c\x7a\x25\xbf\xbc\xc0\xb2\x6a\x0e\x0d\x0a\xc9\x8f\x5f\xa9\x34\xca\x38\x5b\xf2\xc2\xbc\xca\x90\xfd\xe6\x4d\x06\x64\x58\x3d\x77\x63\x4a\x04\x33\xb9\x94\x92\x02\x07\xf7\x44\xac\x2a\x69\x5e\x3a\x8b\xc3\x9e\x68\x01\xa7\x22\x9d\xb9\xb1\x1b\xd0\x75")); 22 | $this->assertEquals('b69fb4ee1dd4d571bb27200ce91d867f8c741b71947e08780a2336b1988b7879', blake256("\x09\x41\x6f\x25\xdc\x67\x7c\x93\x10\xde\x9b\x73\x8e\x37\x05\x9d\x0e\xb1\xe3\xf2\xee\xb6\x50\xb7\x0c\x70\x28\x6f\x87\xc7\xbb\x1f\x2d\x09\xbc\x84\x83\xbd\x30\xb3\x9b\x94\x9a\x9b\x4f\x0f\xce\x85\x39\xf6\x8f\x74\x37\x09\x65\xa4\x80\x05\x29\x23\x86\xad\x06\xb3\x53\xc5\x39\xee\x2e\x04\x31\xc9\x37\xc7\xd1\xec\x5b\xec\x61\xe4\x6a\x18\xb0\xa9\xcb\xdd\x12\x9d\x7d\x12\x61\xcf\x11\x5d\xd8\xb9\x60\x28\x47\xf9\x65\x14\xb5\x0e\xd0\xff\x07\x50\x9d\xeb\x29\x54\xff\x7c\xa5\x4c\xd7\xf8\x87\xd9\xaf\xb8\x1f\xe8\x09\x82\x42\xa2\x98\x24\x72\x54\x12\x93\x56\xb9\x01\x81\x7c\xe9\x96\xea\xdc\x79\x89\xf8\xcd\x2a\x06\x07\x1d\x40\x9b\xa3\xd0\x46\x6d\xb4\xdc\xd8\x9a\x95\x58\x66\x06\x5b\x74\x1a\x17\x13\x38\x7a\xfb\x52\xe5\x2f\xea\x97\x7c\xcc\xa6\x40\x7e\x77\x3b\x39\xe7\x89\x61\xdc\x9d\x08\x85\x43\x42\x9f\x05\x87\x1e\x63\x43\xd4\x21\xf9\x4b\x24\xea\x0d\x85\xe8\xe5\x8d\x2f\xa1\xc7\xd4\x1f\x87\xb5\xf8\xc9\xe8\xb3\xfc\x1d\x27\x7d\x15\xff\x58\xda\xc0\x3c\x10\x13\x7a\x26\xd8\x73\xe4\x82\x1c\xaa\xa8\x26\xbc\x85\xb4\xc4\x5e\xb3\x3d\xfb\x8a\x2c\xfa")); 23 | $this->assertEquals('66a31ff7f1df74038496ce1667bc62afaf7b485971aa8a317d91599429e6a907', blake256("\x73\xfb\x13\xbe\xf3\x2d\x30\xcd\x6a\xb2\x6d\xf1\x3c\x56\xbe\xa3\x75\x2f\x34\x65\xd4\x30\xcc\xda\x4c\x0b\x4b\xc2\xd3\x46\xc7\x3f\x29\xa3\xbb\x38\xff\x21\x0a\xc3\xe3\x01\xda\x8f\x56\x1a\x4b\xdc\x1e\xe6\x15\xfb\xb8\x40\xd0\xc1\x44\x0f\x03\xd1\xbf\x22\xb2\x5b\x40\x58\x9d\x4e\x24\x41\xbb\xea\xfb\x44\xdd\x9c\x82\x8a\x03\x29\xb2\x93\xf7\xcd\x67\x76\xb7\x2c\xb5\x76\xd0\x0a\xd1\x31\x19\x95\x1c\x71\xe8\xf6\xdb\x82\x02\xe0\xca\x51\x29\x5f\x7d\xe8\x0d\x50\x0c\xd9\xb6\xb8\x97\x85\x90\x5c\xbe\xa9\x21\x47\x8f\xeb\xf6\xf9\x4e\x4d\x47\x5f\x16\x93\x09\x71\x8b\x2d\xf2\x9e\x6d\x11\x08\xdb\x2a\x95\xa5\x93\xe0\x6e\xb4\xf8\xd2\x20\x2e\x57\xe7\xbc\x5f\xfa\x37\x44\x92\x2a\x8b\x25\xfe\xc1\x88\x6a\x66\xee\xfa\xed\x1a\x14\x8b\xdb\x06\x85\x19\xfe\xa7\x41\x46\x45\x43\xec\x8e\x9b\xe5\x28\x91\x7e\x06\x9e\x6e\xe1\x9f\x21\xa2\x31\x52\x1d\xd5\x00\xb4\xa4\x1b\x1f\x46\xd6\x6b\xdd\x71\x28\x86\x78\xa8\x86\x7d\x95\xa5\x5a\x05\x63\x1f\x6f\x73\x08\x86\x0c\x88\xcd\xeb\x49\x50\x57\x61\xce\x0b\x1a\x0d\xa6\xc3\x5a\x74\x6d\xc7\x57\x92\x15\x9b\x6a\xd9\x36\x99\x7a\xcb\x28\x2d\x08\xc9\x03\x18\xbb\xe7\x21\xbb\xff\x64\xa9\x6e\x73\xe5\x7d\xa6\x58\x4d\xb1\xef\x75\x58\x62\x99\x5e\xe8\x12\xdc\x4c\x84\x60\xa4\x99\x33\xd3\x73\xb0\xd0\xd9\x61\xd6\x2c\x2f\xfe\xd9\xb5\x77\xb9\xf2\xbe\x54\x1a\xb6\x1b\xfd\x1e\xd1\xd0\x7c\x83\xd7\xcc\x7e\x6f\xfa\x8d\x13\x6b\x36\x90\xfa\x63\x62\x34\x97\x68\xd6\x05\xac\x47\x95\x79\x4e\x4e\x60\x95\x16\x77\xe2\xaa\xe0\xce\x29\x0f\xa7\x7e\x88\x6b\xba\x44\x5f\xb6\xda\xb2\xa2\x2d\xfe\xb6\x46\xed\xd4\x69\xe8\x5e\x41\x23\xc1\x99\xe6\x03\x61\x5a\x8f\xf4\x47\x6c\xca\xd4\xcb\x35\x59\x7c\xbf\xde\x80\x32\xcf\xc9\x05\x0a\x36\x6e\x46\x0f\x5d\xdb\x45\x10\xbc\x04\x37\xb2\x04\xa0\x8f\x56\x56\xf3\xc5\x71\x31\x67\xf5\x37\xfa\x98\x37\x59\x42\xf9\x72\xe7\xec\xfd\x1f\xf5\x16\x95\x5d\x3f\x00\xeb\x92\xaf\xa3\x87\x47\xd5\x25\xdd\x46\xc8\x45\x54\x8a\x56\x4e\xdc\xd8\xa4\x6d\xc8\x56\xeb\xad\xcf\x9c\x1e\x0c\x3f\x29\x8b\x01\xe5\xed\xab\x3e\x47\x8a\xd5\xe7\xcd\xe1\xba\x8f\xf3\x58\x6d\x49\xff\x7b\x4a\xc0\x2c\x3f\x05\x07\x33\x9c\x7d\x8f\x78\x73\x7c\x7a\xbc\xb3\xbf\xa5\xc8\x10")); 24 | $this->assertEquals('6a94a9e7c35d232a4cd9fee70abb654c3591c97dc75bc40c6de5e4d6af71d7b8', blake256("\x0e\xef\x0f\x40\x8f\xdd\xd0\xa7\xb5\x96\xc4\x57\xf1\x1b\x7e\xf8\xeb\x19\x6e\x42\x66\xad\xa7\x9f\x9c\x7c\x69\x30\x47\xd7\x13\xbf\x1c\x01\x29\x22\x52\x13\x20\xae\x40\x84\x09\x15\xe7\x37\x06\xa9\xe6\x2a\x92\x9f\x01\x80\x9c\xc1\x17\xdd\x21\x82\x93\xc0\xf5\x7c\x41\x65\x74\x68\x2a\x57\xdf\x4a\xac\x89\xd5\x9e\xe5\x51\xbf\x3a\xde\xb6\x6f\x22\x86\xee\x60\x9c\x19\x54\xd3\x85\x1f\x73\xf9\xfe\xf2\xe0\x16\x89\xf9\xa6\x4e\xff\x52\x65\xf3\x6b\x6f\x87\x2e\x02\x97\x7b\x96\x64\x07\xc3\x5a\xe5\x95\x99\x22\x54\x43\x0b\xba\xa0\xb9\x4e\xc4\xfb\xc3\x12\x94\xe6\x4c\x4b\x14\x14\x7d\x92\x00\x2b\x28\xef\x47\x65\xb3\x2d\x6e\xb1\x40\xe1\x49\x15\xcb\xa8\x03\x5b\xf0\x6f\xdf\x29\x42\xf6\x5e\x69\x18\xc9\x83\xad\xb4\xec\x5b\x8a\x31\xb9\x17\x8f\xfa\xfe\xb7\x0a\xed\x43\xef\xf4\xef\x19\x43\xad\x99\x17\x31\xfa\x88\x1f\xb3\xcb\xe5\x39\xb1\x83\x58\x98\x18\x71\x3b\x0b\xd4\xc0\x75\xdb\xbd\xc1\x01\x56\x68\xdb\x6e\xce\xd6\x54\xc1\x55\x0c\xb4\xc2\x66\xff\xb8\x4c\xd5\x92\x4e\x07\x69\x77\x61\x74\xfc\xca\x1d\xf4\x8f\x5e\xf2\x52\xff\xf8\x54\x0e\x70\x08\xe3\x31\x1b\x54\x91\xf0\x06\x82\x3a\x9a\x7d\x42\x11\x22\x8b\x08\x93\x6d\xaf\xcc\x58\xbd\x62\x63\xc9\x60\x41\xab\x22\x1a\x0a\x52\xfe\x87\xf1\x4f\x52\x62\x9d\x9e\x98\x21\x48\x87\xd1\xcf\xcb\x8e\xd9\x18\x5b\xeb\xc5\x43\xf6\x0e\x88\xc1\xad\xc2\xaf\x74\x38\xfa\x60\x68\xed\xb5\x92\x8c\xb1\x58\xee\xd4\xc1\x9f\xb4\x4b\x50\x1b\x2b\x02\x0c\x5d\xf6\xde\x09\x1f\xf9\x30\xcf\x2f\x04\xd2\x7d\x93\x5b\x2f\x37\x44\x31\x60\xe8\x6d\x4e\xe0\x46\x44\x18\x4b\x34\xb0\xe5\x76\x3e\xcd\x81\x45\x45\x82\x27\xe6\x69\x47\x81\x25\x24\x02\xa0\xbd\x57\x15\x63\x5a\x69\x91\xaf\x2c\x58\x1f\x99\xfb\xa1\xcb\x8a\xe7\x1c\x27\xb0\x1b\xd6\x9b\x65\x8b\x01\x2e\xd0\xe6\xf9\x8d\x05\x56\x42\x36\x93\xb9\x65\x89\x9d\xfb\x24\xe4\xf0\xd0\xd4\xfe\x8f\x8b\xf8\xef\x7c\x3d\x7e\xee\x03\xa8\x25\x3e\x63\xdf\x7a\xf2\x6a\x6c\x19\x7c\x42\xbc\x11\x5c\x0e\xd3\xb3\x08\xb2\x52\x0a\xe5\x35\xf7\x25\x3a\xb0\x0c\x2e\xde\x9a\x11\x9a\xe4\x4a\x68\x7e\xc6\xf6\xc0\x6e\xd7\x9d\x6a\x99\xff\x9c\x9f\x4d\x80\x8c\x15\x7b\xb7\x68\x3c\x40\x16\x1a\xfd\xdb\xb9\x6b\x98\x2b\x5c\xa4\x62\xf0\x72\xa4\xf5\x5d\xfe\xe3\xd2\x25\x54\x0e\xbd\xaf\x13\xdb\x7a\xc0\x5c\x40\x85\x4c\xa6\xc6\x11\x8a\x65\x71\x10\xca\x8e\x19\xfa\x2e\xc7\xb3\xbc\x64\x89\x0a\xa5\xd6\x9e\x95\xa1\x29\x1d\x57\xdb\xdf\xb3\x51\xb1\xc8\xc6\xbd\x48\xe1\xda\xc7\x85\xda\xdc\xd3\xe0\xf8\xb2\x55\x49\xc4\x90\x3f\xbf\xf4\x0c\x9d\x52\x3e\x43\xd9\x28\xe5\xc1\x55\xce\x9c\x25\xfd\x5c\x56\xdd\xf2\x71\xe8\xde\x2e\xfc\x02\x73\x3d\x6c\x20\x14\x4c\x31\xd4\x3b\x37\xc3\x12\x46\x12\xbd\x85\xda\x4b\x87\x14\x2d\x8e\xe6\x58\xf5\xe1\x80\xc7\x96\x95\xd1\x18\xf3\xe7\xa2\xb6\x3f\xb5\x63\x3f\x53\x42\xb9\x33\x52\x82\x78\xf2\x2d\xdb\x90\xb1\x21\xe3\x8a\x08\x99\x37\x2b\x94\x6c\x8f\xf2\x7a\xb2\x54\x5c\x7e\x34\x16\x51\xdf\xfd\xb5\x03\x04\xdd\x94\x8b\xf8\xcb\x08\xbc\xa2\xee\xc5\x93\xde\xf3\x7e\x0b\x94\x87\xf7\x85\x15\x1b\xb1\xbc\xcc\x08\x74\x15\x9e\x04\xd6\x35\x76\xaa\xf3\xf7\x91\x1e\x71\x57\x60\x34\x67\xd5\xfe\x4c\x31\x40\x8c\xbb\x7a\x91\xbd\x86\x28\x9a\x75\x2c\x52\xf3\x14\xab\xfc\xe4\x6b\x42\x54\xac\xbb\x04\x21\xc9\x15\xa9\x86\x2c\x60\xc4\xeb\x1b\xfc\xd2\x9f\xcf\xa3\xa7\xc6\x7b\x2a\x32\x9d\xf1\xeb\x66\xdf\x84\x88\xfb\x0e\x79\xc6\xfb\x97\x71\x1e\x34\x7c\x39\xc4\x60\x45\xe3\x63\x3c\xe0\x6a\x4b\x7e\x76\xaf\xd8\x8b\xe4\x8c\x3c\x79\x8e\xa6\x10\xb2\xfa\x87\xe0\x22\x23\x45\xa2\x05\x6a\xb6\x9c\x9e\xda\x63\xcc\x6f\xe5\x22\xe6\xcf\x1c\xb6\xc7\xfa\x4c\x95\xc3\x4d\xb3\x59\xf8\x22\xe3\xdf\x52\x5b\x3c\xb1\xb9\xdd\xbb\x29\x2b\xa6\xdb\x75\xa7\xd3\x47\x48\x59\xb0\xa1\xdc\x7e\x6c\x2a\xec\xac\xaa\xb9\x98\xb9\xf0\x67\x96\x72\x64\xc2\x97\x00\xe6\x0a\xff\xd7\x5e\x80\xb6\xc1\x45\x9b\x63\x0b\x33\x8e\x17\x0e\x9a\x8e\xdd\x4a\xc0\x05\xce\xde\xca\xfe\x71\x6d\xe7\xb4\x54\x77\x1a\x8f\xa0\x5a\x10\xf4\x03\x16\x9a\xfc\xc9\x1b\x92\xae\xf0\x72\x98\x3c\xa6\x49\xfb\x99\xc9\x94\xce\x1a\x1b\x84\xea\x30\xaf\x4e\xe4\x7a\x11\x36\x4a\x52\x79\xcc\xa6\xb0\x0a\xcb\x90\x6c\x2f\x5a\x19\xd0\x0a\xed\x2b\x18\x15\xf0\xaf\x64\x46\x36\x5e\x3e\xb6\x03\x3c\x54\x59\x89\x8f\xe6\xc7\x53\x5c\x8e\x26\x09\xc1\x27\xa0\x8a\xb7\x1b\xa3\xbe\x6d\xa9\xf3\x69\x5d\x59\x42\x6e\xed\x16\xeb\xfa\x30\xeb\xbe\x0e\x8f\xb2\xb5\x65\xd1\x17\xe8\x16\x4c\xcb")); 25 | 26 | $this->assertEquals('f340221598d6e02b2f0b8a5b908a90e7a746138301cc702a52107f436fdf4108', blake256(file_get_contents(__DIR__.'/../fixtures/file311bytes.txt'))); 27 | $this->assertEquals('f498fd2f70a781e2be53a7838369074f931134ff2db0633b0c857ec71330ac66', blake256(file_get_contents(__DIR__.'/../fixtures/file313bytes.txt'))); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Crypto/ExtendedKeyTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(32, strlen(ExtendedKey::generateSeed($network))); 16 | $this->assertEquals(16, strlen(ExtendedKey::generateSeed($network, 16))); 17 | $this->assertEquals(64, strlen(ExtendedKey::generateSeed($network, 64))); 18 | } 19 | 20 | public function test_generate_seed_wrong_length() 21 | { 22 | $this->expectExceptionMessage('Invalid seed length. Length should be between 16 and 64 bytes (32 recommended).'); 23 | ExtendedKey::generateSeed(GeneralNetwork::instance(), 2); 24 | } 25 | 26 | public function test_generate_new_master_seed_wrong_size() 27 | { 28 | $this->expectExceptionMessage('Invalid seed length. Length should be between 16 and 64 bytes (32 recommended).'); 29 | ExtendedKey::newMaster('22', GeneralNetwork::instance()); 30 | } 31 | 32 | public function test_bip32_vector1() 33 | { 34 | $seed = hex2bin('000102030405060708090a0b0c0d0e0f'); 35 | $master = ExtendedKey::newMaster($seed, GeneralNetwork::instance()); 36 | 37 | $this->assertEquals( 38 | 'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8', 39 | $master->neuter()->__toString() 40 | ); 41 | $this->assertEquals( 42 | 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi', 43 | $master->__toString() 44 | ); 45 | 46 | // m/0H 47 | $m0H = $master->hardenedChildKey(0); 48 | $this->assertEquals( 49 | 'xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw', 50 | $m0H->neuter()->__toString() 51 | ); 52 | $this->assertEquals( 53 | 'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7', 54 | $m0H->__toString() 55 | ); 56 | 57 | // m/0H/1 58 | $m0H_1 = $m0H->privateChildKey(1); 59 | $this->assertEquals( 60 | 'xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ', 61 | $m0H_1->neuter()->__toString() 62 | ); 63 | $this->assertEquals( 64 | 'xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs', 65 | $m0H_1->__toString() 66 | ); 67 | 68 | // m/0H/1/2H 69 | $m0H_1_2H = $m0H_1->hardenedChildKey(2); 70 | $this->assertEquals( 71 | 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5', 72 | $m0H_1_2H->neuter()->__toString() 73 | ); 74 | $this->assertEquals( 75 | 'xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM', 76 | $m0H_1_2H->__toString() 77 | ); 78 | 79 | // m/0H/1/2H/2 80 | $m0H_1_2H_2 = $m0H_1_2H->privateChildKey(2); 81 | $this->assertEquals( 82 | 'xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV', 83 | $m0H_1_2H_2->neuter()->__toString() 84 | ); 85 | $this->assertEquals( 86 | 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334', 87 | $m0H_1_2H_2->__toString() 88 | ); 89 | 90 | // m/0H/1/2H/2/1000000000 91 | $m0H_1_2H_2_1000000000 = $m0H_1_2H_2->privateChildKey(1000000000); 92 | $this->assertEquals( 93 | 'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy', 94 | $m0H_1_2H_2_1000000000->neuter()->__toString() 95 | ); 96 | $this->assertEquals( 97 | 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76', 98 | $m0H_1_2H_2_1000000000->__toString() 99 | ); 100 | } 101 | 102 | public function test_bip32_vector2() 103 | { 104 | $seed = hex2bin('fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542'); 105 | $master = ExtendedKey::newMaster($seed, GeneralNetwork::instance()); 106 | 107 | $this->assertEquals( 108 | 'xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB', 109 | $master->neuter()->__toString() 110 | ); 111 | $this->assertEquals( 112 | 'xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U', 113 | $master->__toString() 114 | ); 115 | 116 | // m/0 117 | $m0 = $master->privateChildKey(0); 118 | $this->assertEquals( 119 | 'xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH', 120 | $m0->neuter()->__toString() 121 | ); 122 | $this->assertEquals( 123 | 'xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt', 124 | $m0->__toString() 125 | ); 126 | 127 | // m/0/2147483647H 128 | $m0_2147483647H = $m0->hardenedChildKey(2147483647); 129 | $this->assertEquals( 130 | 'xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a', 131 | $m0_2147483647H->neuter()->__toString() 132 | ); 133 | $this->assertEquals( 134 | 'xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9', 135 | $m0_2147483647H->__toString() 136 | ); 137 | 138 | // m/0/2147483647H/1 139 | $m0_2147483647H_1 = $m0_2147483647H->privateChildKey(1); 140 | $this->assertEquals( 141 | 'xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon', 142 | $m0_2147483647H_1->neuter()->__toString() 143 | ); 144 | $this->assertEquals( 145 | 'xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef', 146 | $m0_2147483647H_1->__toString() 147 | ); 148 | 149 | // m/0/2147483647H/1/2147483646H 150 | $m0_2147483647H_1_2147483646H = $m0_2147483647H_1->hardenedChildKey(2147483646); 151 | $this->assertEquals( 152 | 'xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL', 153 | $m0_2147483647H_1_2147483646H->neuter()->__toString() 154 | ); 155 | $this->assertEquals( 156 | 'xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc', 157 | $m0_2147483647H_1_2147483646H->__toString() 158 | ); 159 | 160 | // m/0/2147483647H/1/2147483646H/2 161 | $m0_2147483647H_1_2147483646H_2 = $m0_2147483647H_1_2147483646H->privateChildKey(2); 162 | $this->assertEquals( 163 | 'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt', 164 | $m0_2147483647H_1_2147483646H_2->neuter()->__toString() 165 | ); 166 | $this->assertEquals( 167 | 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j', 168 | $m0_2147483647H_1_2147483646H_2->__toString() 169 | ); 170 | } 171 | 172 | public function test_bip32_vector3() 173 | { 174 | $seed = hex2bin('4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be'); 175 | $master = ExtendedKey::newMaster($seed, GeneralNetwork::instance()); 176 | 177 | $this->assertEquals( 178 | 'xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13', 179 | $master->neuter()->__toString() 180 | ); 181 | $this->assertEquals( 182 | 'xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6', 183 | $master->__toString() 184 | ); 185 | 186 | // m/0H 187 | $m0H = $master->hardenedChildKey(0); 188 | $this->assertEquals( 189 | 'xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y', 190 | $m0H->neuter()->__toString() 191 | ); 192 | $this->assertEquals( 193 | 'xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L', 194 | $m0H->__toString() 195 | ); 196 | } 197 | 198 | public function test_bip32_generate_address() 199 | { 200 | $seed = hex2bin("110131b162cfba8077facb546955d0e45dca7c93c1a27379a4cf166b882508c0"); 201 | $master = ExtendedKey::newMaster($seed, MainNet::instance()); 202 | 203 | $this->assertEquals( 204 | 'dprv3hCznBesA6jBufbh3Y9f5cmEa3rmeNMnr7tzTiEVbYrXmTXgnD9ZrqUPzANojB19ntJqCydHVgcwVne72cd5TJPrr5CnmVxUaUbkA3BcByq', 205 | $master->__toString() 206 | ); 207 | 208 | $purpose = $master->hardenedChildKey(44); 209 | $this->assertEquals( 210 | 'dprv3kvp7kdbp529Dm7YB4Usb3nq4z85ac3a75qa6d3Wjhr1Yxtyt3YjbvpSbZJY5e8AVooC6VKDTpAjkPaTifxc7ZtLU78smZE7qZYQKEqCnCh', 211 | $purpose->__toString() 212 | ); 213 | 214 | $coinType = $purpose->hardenedChildKey(20); 215 | $this->assertEquals( 216 | 'dprv3nar8Jwt7u8Tfh6Q5hZrz1etQyug1p464iKJA4f3pBKWpstdDAn4gttgLW5KFn6hEYvqNH6BJMRgh6ybyYHUbFC98U63RyMhPfrmHfa3K25', 217 | $coinType->__toString() 218 | ); 219 | 220 | $account = $coinType->hardenedChildKey(20); 221 | $this->assertEquals( 222 | 'dprv3oH7V64hFGP5UeYm5eTyDtzz2QyZqEHFkh8HLfjq62T2FyVvzdEhQMhg6iijuduJDndBVscHtURUMwBPrDQ4ckQ6PXgGJBHteRv6k3Nnw9W', 223 | $account->__toString() 224 | ); 225 | 226 | $account0Pri = $account->privateChildKey(0); 227 | $this->assertEquals( 228 | 'dprv3qL8MJLw4yuPBSHWXHvgxgcC9SjEtKcLJgEpZr1k3AyGhHymTWHs5LPvEjJk3yXjt2GkufkQZ6NHurjuJ6S1aHLqx8XxxeRsXqDuEnHJQ64', 229 | $account0Pri->__toString() 230 | ); 231 | 232 | $account0Pub = $account->publicChildKey(0); 233 | $this->assertEquals( 234 | 'dpubZH8DiRuE9MyB5rBGmoz3UuQSmTHWKGCQWDs9Jkx73FZuQr1QLTdU9uuwPRbEgEnMYriY9SUr4XshamuoXZC121HVqPXBSFvE57gG9pZd2Ts', 235 | $account0Pub->__toString() 236 | ); 237 | 238 | $account0Pub = $account0Pri->neuter(); 239 | $this->assertEquals( 240 | 'dpubZH8DiRuE9MyB5rBGmoz3UuQSmTHWKGCQWDs9Jkx73FZuQr1QLTdU9uuwPRbEgEnMYriY9SUr4XshamuoXZC121HVqPXBSFvE57gG9pZd2Ts', 241 | $account0Pub->__toString() 242 | ); 243 | } 244 | 245 | public function test_default_wallet_layout() 246 | { 247 | $masterKey = "dprv3hCznBesA6jBushjx7y9NrfheE4ZshnaKYtsoLXefmLPzrXgEiXkdRMD6UngnmBYZzgNhdEd4K3PidxcaCiR6HC9hmpj8FcrP4Cv7zBwELA"; 248 | 249 | // m 250 | $master = ExtendedKey::fromString($masterKey); 251 | 252 | // m/0H 253 | $acct0 = $master->hardenedChildKey(0); 254 | 255 | // m/0H/0 256 | $acct0Ext = $acct0->privateChildKey(0); 257 | 258 | // m/0H/1 259 | $acct0Int = $acct0->privateChildKey(1); 260 | 261 | // m/0H/0/10 262 | $acct0Ext10 = $acct0Ext->privateChildKey(10); 263 | 264 | // m/0H/1/0 265 | $acct0Int0 = $acct0Int->privateChildKey(0); 266 | 267 | $this->assertEquals('DshMmJ3bfvMDdk1mkXRD3x5xDuPwSxoYGfi', $acct0Ext10->getAddress()); 268 | $this->assertEquals('DsoTyktAyEDkYpgKSex6zx5rrkFDi2gAsHr', $acct0Int0->getAddress()); 269 | 270 | // Neuter the master key to generate a master public extended key. This 271 | // gives the path: 272 | // N(m/*) 273 | $masterNeuter = $master->neuter(); 274 | 275 | $this->assertInstanceOf(MainNet::class, $master->getNetwork()); 276 | 277 | $this->assertEquals( 278 | 'dpubZ9169KDAEUnypHbWCe2Vu5TxGEcqJeNeX6XCYFU1fqw2iQZK7fsMhzsEFArbLmyUdprUw9aXHneUNd92bjc31TqC6sUduMY6PK2z4JXDS8j', 279 | $masterNeuter->__toString() 280 | ); 281 | } 282 | 283 | public function test_derive_wrong_coin_type() 284 | { 285 | $this->expectExceptionMessage('Invalid coin type'); 286 | 287 | $seed = hex2bin('000102030405060708090a0b0c0d0e0f'); 288 | $master = ExtendedKey::newMaster($seed, $network = GeneralNetwork::instance()); 289 | $master->deriveCoinTypeKey(ExtendedKey::MAX_COIN_TYPE + 1); 290 | } 291 | 292 | public function test_derive_hardened_key_from_public() 293 | { 294 | $this->expectExceptionMessage('Cannot derive a hardened key from a public key'); 295 | 296 | $seed = hex2bin('000102030405060708090a0b0c0d0e0f'); 297 | $master = ExtendedKey::newMaster($seed, $network = GeneralNetwork::instance()); 298 | $master->neuter()->neuter()->publicChildKey(ExtendedKey::HARDENED_KEY_START + 1); 299 | } 300 | 301 | public function test_derive_private_from_public_key() 302 | { 303 | $this->expectExceptionMessage('Cannot derive a extended private key from a extended public key'); 304 | 305 | $seed = hex2bin('000102030405060708090a0b0c0d0e0f'); 306 | $master = ExtendedKey::newMaster($seed, $network = GeneralNetwork::instance()); 307 | $master->neuter()->privateKey(); 308 | } 309 | 310 | public function test_wrong_child_num() 311 | { 312 | $this->expectExceptionMessage('Invalid childnum for BIP32 key, must be in range [0 - (2^31)-1] inclusive'); 313 | 314 | $seed = hex2bin('000102030405060708090a0b0c0d0e0f'); 315 | $master = ExtendedKey::newMaster($seed, $network = GeneralNetwork::instance()); 316 | new ExtendedKey( 317 | $master->privateKey(), 318 | $master->chainCode(), 319 | $master->depth(), 320 | $master->parentFP(), 321 | ExtendedKey::HARDENED_KEY_START + ExtendedKey::HARDENED_KEY_START + 20, 322 | $network, 323 | true 324 | ); 325 | } 326 | 327 | public function test_wrong_parent_finger_print() 328 | { 329 | $this->expectExceptionMessage('Invalid fingerprint for BIP32 key, must be in range [0 - (2^31)-1] inclusive'); 330 | 331 | $seed = hex2bin('000102030405060708090a0b0c0d0e0f'); 332 | $master = ExtendedKey::newMaster($seed, $network = GeneralNetwork::instance()); 333 | new ExtendedKey( 334 | $master->privateKey(), 335 | $master->chainCode(), 336 | $master->depth(), 337 | '-2000', 338 | $master->childNum(), 339 | $network, 340 | true 341 | ); 342 | } 343 | 344 | public function test_wrong_depth() 345 | { 346 | $this->expectExceptionMessage('Invalid depth for BIP32 key, must be in range [0 - 255] inclusive'); 347 | 348 | $seed = hex2bin('000102030405060708090a0b0c0d0e0f'); 349 | $master = ExtendedKey::newMaster($seed, $network = GeneralNetwork::instance()); 350 | new ExtendedKey( 351 | $master->privateKey(), 352 | $master->chainCode(), 353 | 256, 354 | $master->parentFP(), 355 | $master->childNum(), 356 | $network, 357 | true 358 | ); 359 | } 360 | 361 | public function test_wrong_chain_code() 362 | { 363 | $this->expectExceptionMessage('Chain code should be 32 bytes'); 364 | 365 | $seed = hex2bin('000102030405060708090a0b0c0d0e0f'); 366 | $master = ExtendedKey::newMaster($seed, $network = GeneralNetwork::instance()); 367 | new ExtendedKey( 368 | $master->privateKey(), 369 | $master->chainCode() . 'fasdfasd', 370 | $master->depth(), 371 | $master->parentFP(), 372 | $master->childNum(), 373 | $network, 374 | true 375 | ); 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /tests/Crypto/NetworkTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('tprvZUo1ZuEfLLFWfAYiMVaoDV1EeLmbSRuNzaSh7F4awft7dm8nHfFAFZyobWQyV8Qr26r8M2CmNw6nEb35HaECWFGy1vzx2ZGdyfBeaaHudoi', (string) $master); 19 | $this->assertEquals('tpubVhnMyQmZAhoosedBTX7oacwyCNc5qtdEMoNHudUCW1R6WZTvqCZQoNJHSn4H11puwdk4qyDv2ET637EDap4r8HH3odjBC5nEjmnPcsDfLwm', (string) $master->neuter()); 20 | $this->assertEquals('TsiBhMy6LYKGkHeWbPYEPFMu9VgqwRgkt8T', $master->getAddress()); 21 | } 22 | 23 | public function test_network_factory() 24 | { 25 | $this->assertInstanceOf(TestNet::class, NetworkFactory::fromExtendedKeyVersion(TestNet::HD_PRIVATE_KEY_ID)); 26 | $this->assertInstanceOf(TestNet::class, NetworkFactory::fromExtendedKeyVersion(TestNet::HD_PUBLIC_KEY_ID)); 27 | $this->assertInstanceOf(MainNet::class, NetworkFactory::fromExtendedKeyVersion(MainNet::HD_PRIVATE_KEY_ID)); 28 | $this->assertInstanceOf(MainNet::class, NetworkFactory::fromExtendedKeyVersion(MainNet::HD_PUBLIC_KEY_ID)); 29 | 30 | $testnet = TestNet::instance(); 31 | 32 | $dataClient = $testnet->getDataClient(); 33 | $this->assertInstanceOf(DataClient::class, $dataClient); 34 | 35 | $mainnet = MainNet::instance(); 36 | 37 | $dataClient = $mainnet->getDataClient(); 38 | $this->assertInstanceOf(DataClient::class, $dataClient); 39 | } 40 | 41 | public function test_network_factory_exception() 42 | { 43 | $this->expectExceptionMessage('Unknown extended key version.'); 44 | NetworkFactory::fromExtendedKeyVersion('fasdfasdf'); 45 | } 46 | 47 | public function test_extended_key_wrong_checksum() 48 | { 49 | $this->expectExceptionMessage('Wrong checksum on encoding extended key!'); 50 | 51 | $network = TestNet::instance(); 52 | $network->extendedKeyBase58Decode('dprv3hCznBesA6jBushjD7y9NrfheE4ZshnaKYtsoLXefmLPzrXgEiXkdRMD6UngnmBYZzgNhdEd4K3PidxcaCiR6HC9hmpj8FcrP4Cv7zBwELA'); 53 | } 54 | 55 | public function test_extended_key_wrong_encode() 56 | { 57 | $this->expectExceptionMessage('Wrong key length.'); 58 | 59 | $network = TestNet::instance(); 60 | $network->extendedKeyBase58Decode($network->base58()->encode('fasdfadf')); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/Data/DataClientTest.php: -------------------------------------------------------------------------------- 1 | httpMock = $this->getMockBuilder(Client::class) 28 | ->setMethods(['request']) 29 | ->getMock(); 30 | $this->client = new DataClient(null); 31 | $this->client->setGuzzle($this->httpMock); 32 | } 33 | 34 | public function test_address_raw() 35 | { 36 | $this->httpMock->expects($this->exactly(2)) 37 | ->method('request') 38 | ->with('get', $this->equalTo('/api/address/TseQDDfk4xDvz2R92cexf1WaAvUGXEfRJ75/raw')) 39 | ->willReturn($this->getFixtureJson('api_address_TseQDDfk4xDvz2R92cexf1WaAvUGXEfRJ75_raw.json')); 40 | 41 | $transactions = $this->client->getAddressRaw('TseQDDfk4xDvz2R92cexf1WaAvUGXEfRJ75'); 42 | 43 | $this->assertCount(1, $transactions); 44 | $this->assertInstanceOf(Transaction::class, $transactions[0]); 45 | $this->assertEquals('2cf8880d4d9fd925381046ab28ca15c77e4a4b9e935da65f607d1afd27b33119', $transactions[0]->getTxid()); 46 | $this->assertEquals(5847, $transactions[0]->getConfirmations()); 47 | $this->assertEquals(0.14509833, $transactions[0]->getOutAmount('TseQDDfk4xDvz2R92cexf1WaAvUGXEfRJ75')); 48 | 49 | $transactions = $this->client->getAddressRaw('TseQDDfk4xDvz2R92cexf1WaAvUGXEfRJ75', new \DateTime()); 50 | $this->assertCount(0, $transactions); 51 | } 52 | 53 | public function test_request_exception() 54 | { 55 | $this->httpMock->expects($this->once()) 56 | ->method('request') 57 | ->with('get', $this->equalTo('/api/address/TseQDDfk4xDvz2R92cexf1WaAvUGXEfRJ75/raw')) 58 | ->willThrowException(new \Exception()); 59 | 60 | $this->assertEquals(false, $this->client->getAddressRaw('TseQDDfk4xDvz2R92cexf1WaAvUGXEfRJ75')); 61 | } 62 | 63 | /** 64 | * @param string $filename 65 | * 66 | * @return array 67 | */ 68 | protected function getFixtureJson($filename) 69 | { 70 | return new Response(200, [], file_get_contents(__DIR__.'/../fixtures/dataclient/'.$filename)); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/Data/TransactionTest.php: -------------------------------------------------------------------------------- 1 | 'test', 'time' => 'test']); 11 | $this->assertEquals(0, $transaction->getConfirmations()); 12 | } 13 | 14 | public function test_transaction_wrong_data() 15 | { 16 | $this->expectExceptionMessage('Wrong transaction data!'); 17 | new Transaction([]); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/Mock/GeneralNetwork.php: -------------------------------------------------------------------------------- 1 | 100.84993]]; 14 | $response = new Response(200, [], json_encode($data)); 15 | 16 | /** @var Client $clientMock */ 17 | $clientMock = $this->createMock(Client::class); 18 | $clientMock->method('request')->willReturn($response); 19 | 20 | $rate = CoinMarketCap::getRate(); 21 | 22 | /** @var CoinMarketCap $provider */ 23 | $provider = $rate->getProvider(); 24 | $provider->setClient($clientMock); 25 | 26 | $this->assertInstanceOf(Rate::class, $rate); 27 | $this->assertEquals('USD', $rate->getCurrency()); 28 | 29 | $amountDCR = $rate->convertToCrypto(7.09); 30 | $this->assertTrue(is_numeric($amountDCR)); 31 | $this->assertEquals(0.00991572, $rate->getToRate(), '', 0.00000001); 32 | $this->assertEquals(100.8499, $rate->getFromRate(), '', 0.00000001); 33 | $this->assertEquals(0.0703025, $amountDCR, '', 0.00000001); 34 | $this->assertEquals(7.09, $rate->convertToFiat($amountDCR), '', 0.0001); 35 | 36 | $amountDCR = $rate->convertToCrypto(10.137); 37 | $this->assertTrue(is_numeric($amountDCR)); 38 | $this->assertEquals(0.00991572, $rate->getToRate(), '', 0.00000001); 39 | $this->assertEquals(100.8499, $rate->getFromRate(), '', 0.00000001); 40 | $this->assertEquals(0.10051572, $amountDCR, '', 0.00000001); 41 | $this->assertEquals(10.137, $rate->convertToFiat($amountDCR), '', 0.0001); 42 | } 43 | 44 | public function test_no_price() 45 | { 46 | $this->expectExceptionMessage('Missing currency rate USD!'); 47 | 48 | /** @var Client $clientMock */ 49 | $clientMock = $this->createMock(Client::class); 50 | $clientMock->method('request')->willReturn(new Response(200, [], json_encode([[]]))); 51 | 52 | $service = new CoinMarketCap(); 53 | $service->setClient($clientMock); 54 | $service->getPrice(); 55 | } 56 | 57 | public function test_failed_request() 58 | { 59 | $this->expectExceptionMessage('Fetching rates request failed!'); 60 | 61 | /** @var Client $clientMock */ 62 | $clientMock = $this->createMock(Client::class); 63 | $clientMock->method('request')->willReturn(new Response(400)); 64 | 65 | $service = new CoinMarketCap(); 66 | $service->setClient($clientMock); 67 | $service->getPrice(); 68 | } 69 | 70 | public function test_wrong_response() 71 | { 72 | $this->expectExceptionMessage('Get wrong response on rates requests!'); 73 | 74 | /** @var Client $clientMock */ 75 | $clientMock = $this->createMock(Client::class); 76 | $clientMock->method('request')->willReturn(new Response()); 77 | 78 | $service = new CoinMarketCap(); 79 | $service->setClient($clientMock); 80 | $service->getPrice(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/fixtures/dataclient/api_address_TseQDDfk4xDvz2R92cexf1WaAvUGXEfRJ75_raw.json: -------------------------------------------------------------------------------- 1 | [{"size":251,"txid":"3881506f0f2c15a1a9c9c1e4c199c6dcdda456da92883f4a7c7759f52c1c2164","version":1,"locktime":0,"vin":[{"txid":"2cf8880d4d9fd925381046ab28ca15c77e4a4b9e935da65f607d1afd27b33119","vout":0,"tree":0,"amountin":0.14509833,"blockheight":249264,"blockindex":2,"scriptSig":{"asm":"30440220276438e9b17701960fe04a13da261ad15a3a3b9c3cd6a82da71b0400d96aae50022006649d0c7a25b9fcc4c9b8cafa7861dc385cea1b984306475f2ae914863da99b01 03700d19b1ca092db791fa0054989160dcbe74331cca15c9e4b26eff6c41348aef","hex":"4730440220276438e9b17701960fe04a13da261ad15a3a3b9c3cd6a82da71b0400d96aae50022006649d0c7a25b9fcc4c9b8cafa7861dc385cea1b984306475f2ae914863da99b012103700d19b1ca092db791fa0054989160dcbe74331cca15c9e4b26eff6c41348aef"},"prevOut":{"addresses":["TseQDDfk4xDvz2R92cexf1WaAvUGXEfRJ75"],"value":0.14509833},"sequence":4294967295}],"vout":[{"value":0.07115717,"n":0,"version":0,"scriptPubKey":{"asm":"OP_DUP OP_HASH160 3161d58d0b62142c4d5fb0138b9e4ab04df90060 OP_EQUALVERIFY OP_CHECKSIG","reqSigs":1,"type":"pubkeyhash","addresses":["TsVXEnbdQVA6D5KJmyT4Ua7pPM9jFtHckP3"]}},{"value":0.07368816,"n":1,"version":0,"scriptPubKey":{"asm":"OP_DUP OP_HASH160 77190a66640445bfd615a1ce8a7c1477d8ac78ae OP_EQUALVERIFY OP_CHECKSIG","reqSigs":1,"type":"pubkeyhash","addresses":["TsbsrtEvVnbyBFy5Z47uW354VS1aMgGNZg2"]}}],"confirmations":2115,"blockhash":"00000000029e40e0ba5515f68c988264dcdac9a709d40e63f1cb93ec61872d48","time":1520278999,"blocktime":1520278999},{"size":251,"txid":"2cf8880d4d9fd925381046ab28ca15c77e4a4b9e935da65f607d1afd27b33119","version":1,"locktime":0,"vin":[{"txid":"6286dac1995da99f978cf3bb770ff302347850c5837a3f0a9a7d7400a32e900a","vout":1,"tree":0,"amountin":2,"blockheight":249262,"blockindex":6,"scriptSig":{"asm":"3044022002178352fe878b25a52b41454109ce4f7c5d0337d34538d4543528a1d3260ffe02200590c3354fca621ee30f7f8a9b5b8acd93eaa00c17797f750ecd0bf83bbf0dea01 034dad27d3687f25e9b42558956cbd13ae035d942b9e64735608524945b8447d8d","hex":"473044022002178352fe878b25a52b41454109ce4f7c5d0337d34538d4543528a1d3260ffe02200590c3354fca621ee30f7f8a9b5b8acd93eaa00c17797f750ecd0bf83bbf0dea0121034dad27d3687f25e9b42558956cbd13ae035d942b9e64735608524945b8447d8d"},"prevOut":{"addresses":["TsdoAsthVEQMpzs1bz2TBHhFwDhPFVeqt3Z"],"value":2},"sequence":4294967295}],"vout":[{"value":0.14509833,"n":0,"version":0,"scriptPubKey":{"asm":"OP_DUP OP_HASH160 92c6beee072ca91fe5621000ede105577ff07434 OP_EQUALVERIFY OP_CHECKSIG","reqSigs":1,"type":"pubkeyhash","addresses":["TseQDDfk4xDvz2R92cexf1WaAvUGXEfRJ75"]}},{"value":1.85464867,"n":1,"version":0,"scriptPubKey":{"asm":"OP_DUP OP_HASH160 059e3183f23f3692afa47fda970801c84fb60d07 OP_EQUALVERIFY OP_CHECKSIG","reqSigs":1,"type":"pubkeyhash","addresses":["TsRXqMzQVdbA52tJ6sdcZCwh1BBg8AD9RjX"]}}],"confirmations":5847,"blockhash":"0000000002a6ed6ed7999761bbd9325a2c1b3026c75ea641e10892bde5d67eae","time":1519826428,"blocktime":1519826428}] 2 | -------------------------------------------------------------------------------- /tests/fixtures/file311bytes.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decred/decred-php-api/c856df3ef56ce1f5c8b1cb791c3dade958b90386/tests/fixtures/file311bytes.txt -------------------------------------------------------------------------------- /tests/fixtures/file313bytes.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decred/decred-php-api/c856df3ef56ce1f5c8b1cb791c3dade958b90386/tests/fixtures/file313bytes.txt --------------------------------------------------------------------------------