diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml new file mode 100644 index 0000000..f8a94a5 --- /dev/null +++ b/.github/workflows/php.yml @@ -0,0 +1,47 @@ +name: PHP + +on: ["push", "pull_request"] + +jobs: + build_and_test: + name: Build and test ethereum-tx with ${{ matrix.php-version }} + strategy: + matrix: + php-version: ["7.3", "7.4", "8.0"] + + runs-on: ubuntu-latest + + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + + - name: PHP version + run: | + php --version + + - uses: actions/checkout@v2 + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v2 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + if: steps.composer-cache.outputs.cache-hit != 'true' + run: composer install --prefer-dist --no-progress --no-suggest + + - name: Run test suite + run: vendor/bin/phpunit --coverage-clover=coverage.xml + + - uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index c422267..8b7f132 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ composer.phar /vendor/ - +composer.lock +.DS_Store +.phpunit.result.cache # Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file # composer.lock diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7ff137e..0000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -sudo: required - -language: php - -php: - - 7.1 - - 7.2 - - 7.3 - -install: - - composer install - -notifications: - email: - recipients: - - alk03073135@gmail.com - on_success: always - on_failure: always - -script: - - vendor/bin/phpunit --coverage-clover=coverage.xml - -after_success: - - bash <(curl -s https://codecov.io/bash) -t 284c5fc4-2109-4bb3-822f-82026d67b1bd diff --git a/README.md b/README.md index d369663..1d9746c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # ethereum-tx -[![Build Status](https://travis-ci.org/web3p/ethereum-tx.svg?branch=master)](https://travis-ci.org/web3p/ethereum-tx) +[![PHP](https://github.com/web3p/ethereum-tx/actions/workflows/php.yml/badge.svg)](https://github.com/web3p/ethereum-tx/actions/workflows/php.yml) [![codecov](https://codecov.io/gh/web3p/ethereum-tx/branch/master/graph/badge.svg)](https://codecov.io/gh/web3p/ethereum-tx) Ethereum transaction library in PHP. @@ -12,7 +12,7 @@ composer require web3p/ethereum-tx # Usage -Create a transaction: +## Create a transaction ```php use Web3p\EthereumTx\Transaction; @@ -24,7 +24,7 @@ $transaction = new Transaction([ 'gas' => '0x76c0', 'gasPrice' => '0x9184e72a000', 'value' => '0x9184e72a', - 'data' => '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675' + 'data' => '' ]); // with chainId @@ -43,122 +43,52 @@ $transaction = new Transaction([ $transaction = new Transaction('0xf86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83'); ``` -Sign a transaction: -```php -use Web3p\EthereumTx\Transaction; - -$signedTransaction = $transaction->sign('your private key'); -``` - -# API - -### Web3p\EthereumTx\Transaction - -#### sha3 - -Returns keccak256 encoding of given data. - -> It will be removed in the next version. - -`sha3(string $input)` - -String input - -###### Example - -* Encode string. - +## Create a EIP1559 transaction ```php -use Web3p\EthereumTx\Transaction; +use Web3p\EthereumTx\EIP1559Transaction; -$transaction = new Transaction([ +// generate transaction instance with transaction parameters +$transaction = new EIP1559Transaction([ 'nonce' => '0x01', 'from' => '0xb60e8dd61c5d32be8058bb8eb970870f07233155', 'to' => '0xd46e8dd67c5d32be8058bb8eb970870f07244567', + 'maxPriorityFeePerGas' => '0x9184e72a000', + 'maxFeePerGas' => '0x9184e72a000', 'gas' => '0x76c0', - 'gasPrice' => '0x9184e72a000', 'value' => '0x9184e72a', - 'data' => '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675' + 'chainId' => 1, // required + 'accessList' => [], + 'data' => '' ]); -$hashedString = $transaction->sha3('web3p'); ``` -#### serialize - -Returns recursive length prefix encoding of transaction data. - -`serialize()` - -###### Example - -* Serialize the transaction data. - +## Create a EIP2930 transaction: ```php -use Web3p\EthereumTx\Transaction; +use Web3p\EthereumTx\EIP2930Transaction; -$transaction = new Transaction([ +// generate transaction instance with transaction parameters +$transaction = new EIP2930Transaction([ 'nonce' => '0x01', 'from' => '0xb60e8dd61c5d32be8058bb8eb970870f07233155', 'to' => '0xd46e8dd67c5d32be8058bb8eb970870f07244567', 'gas' => '0x76c0', - 'gasPrice' => '0x9184e72a000', 'value' => '0x9184e72a', - 'data' => '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675' + 'chainId' => 1, // required + 'accessList' => [], + 'data' => '' ]); -$serializedTx = $transaction->serialize(); ``` -#### sign - -Returns signed of transaction data. - -`sign(string $privateKey)` - -String privateKey - hexed private key with zero prefixed. - -###### Example - -* Sign the transaction data. - +## Sign a transaction: ```php use Web3p\EthereumTx\Transaction; -$transaction = new Transaction([ - 'nonce' => '0x01', - 'from' => '0xb60e8dd61c5d32be8058bb8eb970870f07233155', - 'to' => '0xd46e8dd67c5d32be8058bb8eb970870f07244567', - 'gas' => '0x76c0', - 'gasPrice' => '0x9184e72a000', - 'value' => '0x9184e72a', - 'data' => '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675' -]); -$signedTx = $transaction->sign($stringPrivateKey); +$signedTransaction = $transaction->sign('your private key'); ``` -#### hash - -Returns keccak256 encoding of serialized transaction data. - -`hash()` - -###### Example - -* Hash serialized transaction data. - -```php -use Web3p\EthereumTx\Transaction; +# API -$transaction = new Transaction([ - 'nonce' => '0x01', - 'from' => '0xb60e8dd61c5d32be8058bb8eb970870f07233155', - 'to' => '0xd46e8dd67c5d32be8058bb8eb970870f07244567', - 'gas' => '0x76c0', - 'gasPrice' => '0x9184e72a000', - 'value' => '0x9184e72a', - 'data' => '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675' -]); -$hashedTx = $transaction->serialize(); -``` +https://www.web3p.xyz/ethereumtx.html # License MIT diff --git a/composer.json b/composer.json index 3daaf81..d3fc310 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ } ], "require-dev": { - "phpunit/phpunit": "^6.1" + "phpunit/phpunit": "~7|~8.0" }, "autoload": { "psr-4": { @@ -23,8 +23,9 @@ } }, "require": { - "web3p/rlp": "0.3.2", - "web3p/ethereum-util": "~0.1.2", + "PHP": "^7.1|^8.0", + "web3p/rlp": "0.3.5", + "web3p/ethereum-util": "~0.1.3", "kornrunner/keccak": "~1", "simplito/elliptic-php": "~1.0.6" } diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 9249fb7..0000000 --- a/composer.lock +++ /dev/null @@ -1,1796 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "de28da405ac699ffb57506b1f832536f", - "packages": [ - { - "name": "kornrunner/keccak", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/kornrunner/php-keccak.git", - "reference": "ad761f528f4d1e3ce378b8a0841e5f82c9973535" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/kornrunner/php-keccak/zipball/ad761f528f4d1e3ce378b8a0841e5f82c9973535", - "reference": "ad761f528f4d1e3ce378b8a0841e5f82c9973535", - "shasum": "" - }, - "require": { - "php": ">=7.1.0", - "symfony/polyfill-mbstring": "^1.8" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "~7" - }, - "type": "library", - "autoload": { - "psr-4": { - "kornrunner\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Boris Momcilovic", - "homepage": "https://github.com/kornrunner/php-keccak" - } - ], - "description": "Pure PHP implementation of Keccak", - "keywords": [ - "keccak", - "sha-3", - "sha3-256" - ], - "time": "2018-07-30T22:16:23+00:00" - }, - { - "name": "simplito/bigint-wrapper-php", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/simplito/bigint-wrapper-php.git", - "reference": "cf21ec76d33f103add487b3eadbd9f5033a25930" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/simplito/bigint-wrapper-php/zipball/cf21ec76d33f103add487b3eadbd9f5033a25930", - "reference": "cf21ec76d33f103add487b3eadbd9f5033a25930", - "shasum": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "BI\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Simplito Team", - "email": "s.smyczynski@simplito.com", - "homepage": "https://simplito.com" - } - ], - "description": "Common interface for php_gmp and php_bcmath modules", - "time": "2018-02-27T12:38:08+00:00" - }, - { - "name": "simplito/bn-php", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/simplito/bn-php.git", - "reference": "e852fcd27e4acbc32459606d7606e45a85e42465" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/simplito/bn-php/zipball/e852fcd27e4acbc32459606d7606e45a85e42465", - "reference": "e852fcd27e4acbc32459606d7606e45a85e42465", - "shasum": "" - }, - "require": { - "simplito/bigint-wrapper-php": "~1.0.0" - }, - "require-dev": { - "phpunit/phpunit": "*" - }, - "type": "library", - "autoload": { - "psr-4": { - "BN\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Simplito Team", - "email": "s.smyczynski@simplito.com", - "homepage": "https://simplito.com" - } - ], - "description": "Big number implementation compatible with bn.js", - "time": "2018-04-12T11:07:43+00:00" - }, - { - "name": "simplito/elliptic-php", - "version": "1.0.6", - "source": { - "type": "git", - "url": "https://github.com/simplito/elliptic-php.git", - "reference": "15652609aa55968d56685c2a9120535ccdc00fd9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/simplito/elliptic-php/zipball/15652609aa55968d56685c2a9120535ccdc00fd9", - "reference": "15652609aa55968d56685c2a9120535ccdc00fd9", - "shasum": "" - }, - "require": { - "ext-gmp": "*", - "simplito/bn-php": "~1.1.0" - }, - "require-dev": { - "phpbench/phpbench": "@dev", - "phpunit/phpunit": "*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Elliptic\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Simplito Team", - "email": "s.smyczynski@simplito.com", - "homepage": "https://simplito.com" - } - ], - "description": "Fast elliptic curve cryptography", - "homepage": "https://github.com/simplito/elliptic-php", - "keywords": [ - "Curve25519", - "ECDSA", - "Ed25519", - "EdDSA", - "cryptography", - "curve", - "ecc", - "ecdh", - "elliptic", - "nistp192", - "nistp224", - "nistp256", - "nistp384", - "nistp521", - "secp256k1" - ], - "time": "2019-11-14T13:43:07+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.9.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8", - "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.9-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "time": "2018-08-06T14:22:27+00:00" - }, - { - "name": "web3p/ethereum-util", - "version": "0.1.2", - "source": { - "type": "git", - "url": "https://github.com/web3p/ethereum-util.git", - "reference": "c7b5edd1a3ca20d0dae93f6fb9d253c61dfb73c6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/web3p/ethereum-util/zipball/c7b5edd1a3ca20d0dae93f6fb9d253c61dfb73c6", - "reference": "c7b5edd1a3ca20d0dae93f6fb9d253c61dfb73c6", - "shasum": "" - }, - "require": { - "kornrunner/keccak": "~1", - "php": "^7.1", - "simplito/elliptic-php": "~1.0.6" - }, - "require-dev": { - "phpunit/phpunit": "^6.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Web3p\\EthereumUtil\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "sc0Vu", - "email": "alk03073135@gmail.com" - } - ], - "description": "A collection of utility functions for Ethereum written in PHP.", - "time": "2019-11-23T08:43:24+00:00" - }, - { - "name": "web3p/rlp", - "version": "0.3.2", - "source": { - "type": "git", - "url": "https://github.com/web3p/rlp.git", - "reference": "bc9e29f7a1f658408f25322ea21ff5687a851470" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/web3p/rlp/zipball/bc9e29f7a1f658408f25322ea21ff5687a851470", - "reference": "bc9e29f7a1f658408f25322ea21ff5687a851470", - "shasum": "" - }, - "require-dev": { - "phpunit/phpunit": "~7" - }, - "type": "library", - "autoload": { - "psr-4": { - "Web3p\\RLP\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "sc0Vu", - "email": "alk03073135@gmail.com" - } - ], - "description": "Recursive Length Prefix Encoding in PHP.", - "time": "2019-11-23T08:20:30+00:00" - } - ], - "packages-dev": [ - { - "name": "doctrine/instantiator", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "^6.2.3", - "squizlabs/php_codesniffer": "^3.0.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", - "keywords": [ - "constructor", - "instantiate" - ], - "time": "2017-07-22T11:58:36+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.8.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "replace": { - "myclabs/deep-copy": "self.version" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "time": "2018-06-11T23:09:50+00:00" - }, - { - "name": "phar-io/manifest", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "phar-io/version": "^1.0.1", - "php": "^5.6 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2017-03-05T18:14:27+00:00" - }, - { - "name": "phar-io/version", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "time": "2017-03-05T17:38:23+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "time": "2017-09-11T18:02:19+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "4.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08", - "shasum": "" - }, - "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "doctrine/instantiator": "~1.0.5", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-30T07:14:17+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", - "shasum": "" - }, - "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "time": "2017-07-14T14:27:02+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "1.8.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8.x-dev" - } - }, - "autoload": { - "psr-0": { - "Prophecy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2018-08-05T17:53:17+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "5.3.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^7.0", - "phpunit/php-file-iterator": "^1.4.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0.1", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-xdebug": "^2.5.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.3.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2018-04-06T15:36:58+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.4.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2017-11-27T13:52:08+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.9", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2017-02-26T11:10:40+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.2.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2017-11-27T05:48:46+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "6.5.11", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "7bab54cb366076023bbf457a2a0d513332cd40f2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7bab54cb366076023bbf457a2a0d513332cd40f2", - "reference": "7bab54cb366076023bbf457a2a0d513332cd40f2", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "^1.6.1", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", - "php": "^7.0", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", - "phpunit/php-file-iterator": "^1.4.3", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.9", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", - "sebastian/environment": "^3.1", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^1.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.5.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2018-08-07T07:05:35+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "5.0.10", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.5", - "php": "^7.0", - "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.1" - }, - "conflict": { - "phpunit/phpunit": "<6.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5.11" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "abandoned": true, - "time": "2018-08-09T05:50:03+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" - }, - { - "name": "sebastian/comparator", - "version": "2.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "time": "2018-02-01T13:46:46+00:00" - }, - { - "name": "sebastian/diff", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "time": "2017-08-03T08:09:46+00:00" - }, - { - "name": "sebastian/environment", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "time": "2017-07-01T08:51:00+00:00" - }, - { - "name": "sebastian/exporter", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "time": "2017-04-03T13:19:02+00:00" - }, - { - "name": "sebastian/global-state", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "time": "2017-04-27T15:39:26+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28T20:34:47+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2017-04-07T12:08:54+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2018-01-29T19:49:41+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [] -} diff --git a/phpunit.xml b/phpunit.xml index e2c2311..379e15c 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -7,8 +7,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="false" - syntaxCheck="false"> + stopOnFailure="false"> ./test/unit diff --git a/src/EIP1559Transaction.php b/src/EIP1559Transaction.php new file mode 100644 index 0000000..92b1abe --- /dev/null +++ b/src/EIP1559Transaction.php @@ -0,0 +1,240 @@ + + * + * @author Peter Lai + * @license MIT + */ + +namespace Web3p\EthereumTx; + +use InvalidArgumentException; +use RuntimeException; +use Web3p\RLP\RLP; +use Elliptic\EC; +use Elliptic\EC\KeyPair; +use ArrayAccess; +use Web3p\EthereumUtil\Util; +use Web3p\EthereumTx\TypeTransaction; + +/** + * It's a instance for generating/serializing ethereum eip1559 transaction. + * + * ```php + * use Web3p\EthereumTx\EIP1559Transaction; + * + * // generate transaction instance with transaction parameters + * $transaction = new EIP1559Transaction([ + * 'nonce' => '0x01', + * 'from' => '0xb60e8dd61c5d32be8058bb8eb970870f07233155', + * 'to' => '0xd46e8dd67c5d32be8058bb8eb970870f07244567', + * 'maxPriorityFeePerGas' => '0x9184e72a000', + * 'maxFeePerGas' => '0x9184e72a000', + * 'gas' => '0x76c0', + * 'value' => '0x9184e72a', + * 'chainId' => 1, // required + * 'accessList' => [], + * 'data' => '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675' + * ]); + * + * // generate transaction instance with hex encoded transaction + * $transaction = new EIP1559Transaction('0x02f86c04158504a817c8008504a817c8008252089435353535353535353535353535353535353535358080c080a03fd48c8a173e9669c33cb5271f03b1af4f030dc8315be8ec9442b7fbdde893c8a010af381dab1df3e7012a3c8421d65a810859a5dd9d58991ad7c07f12d0c651c7'); + * ``` + * + * ```php + * After generate transaction instance, you can sign transaction with your private key. + * + * $signedTransaction = $transaction->sign('your private key'); + * ``` + * + * Then you can send serialized transaction to ethereum through http rpc with web3.php. + * ```php + * $hashedTx = $transaction->serialize(); + * ``` + * + * @author Peter Lai + * @link https://www.web3p.xyz + * @filesource https://github.com/web3p/ethereum-tx + */ +class EIP1559Transaction extends TypeTransaction +{ + /** + * Attribute map for keeping order of transaction key/value + * + * @var array + */ + protected $attributeMap = [ + 'from' => [ + 'key' => -1 + ], + 'chainId' => [ + 'key' => 0 + ], + 'nonce' => [ + 'key' => 1, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'maxPriorityFeePerGas' => [ + 'key' => 2, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'maxFeePerGas' => [ + 'key' => 3, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'gasLimit' => [ + 'key' => 4, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'gas' => [ + 'key' => 4, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'to' => [ + 'key' => 5, + 'length' => 20, + 'allowZero' => true, + ], + 'value' => [ + 'key' => 6, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'data' => [ + 'key' => 7, + 'allowLess' => true, + 'allowZero' => true + ], + 'accessList' => [ + 'key' => 8, + 'allowLess' => true, + 'allowZero' => true, + 'allowArray' => true + ], + 'v' => [ + 'key' => 9, + 'allowZero' => true + ], + 'r' => [ + 'key' => 10, + 'length' => 32, + 'allowZero' => true + ], + 's' => [ + 'key' => 11, + 'length' => 32, + 'allowZero' => true + ] + ]; + + /** + * Transaction type + * + * @var string + */ + protected $transactionType = '02'; + + /** + * construct + * + * @param array|string $txData + * @return void + */ + public function __construct($txData=[]) + { + parent::__construct($txData); + } + + /** + * RLP serialize the ethereum transaction. + * + * @return string hex encoded of the serialized ethereum transaction + */ + public function serialize() + { + // sort tx data + if (ksort($this->txData) !== true) { + throw new RuntimeException('Cannot sort tx data by keys.'); + } + $txData = array_fill(0, 12, ''); + foreach ($this->txData as $key => $data) { + if ($key >= 0) { + $txData[$key] = $data; + } + } + $transactionType = $this->transactionType; + return $transactionType . $this->rlp->encode($txData); + } + + /** + * Sign the transaction with given hex encoded private key. + * + * @param string $privateKey hex encoded private key + * @return string hex encoded signed ethereum transaction + */ + public function sign(string $privateKey) + { + if ($this->util->isHex($privateKey)) { + $privateKey = $this->util->stripZero($privateKey); + $ecPrivateKey = $this->secp256k1->keyFromPrivate($privateKey, 'hex'); + } else { + throw new InvalidArgumentException('Private key should be hex encoded string'); + } + $txHash = $this->hash(); + $signature = $ecPrivateKey->sign($txHash, [ + 'canonical' => true + ]); + $r = $signature->r; + $s = $signature->s; + $v = $signature->recoveryParam; + + $this->offsetSet('r', '0x' . $r->toString(16)); + $this->offsetSet('s', '0x' . $s->toString(16)); + $this->offsetSet('v', $v); + $this->privateKey = $ecPrivateKey; + + return $this->serialize(); + } + + /** + * Return hash of the ethereum transaction with/without signature. + * + * @param bool $includeSignature hash with signature + * @return string hex encoded hash of the ethereum transaction + */ + public function hash(bool $includeSignature=false) + { + // sort tx data + if (ksort($this->txData) !== true) { + throw new RuntimeException('Cannot sort tx data by keys.'); + } + if ($includeSignature) { + $length = 12; + } else { + $length = 9; + } + $rawTxData = array_fill(0, $length, ''); + for ($key = 0; $key < $length; $key++) { + if (isset($this->txData[$key])) { + $rawTxData[$key] = $this->txData[$key]; + } + } + $serializedTx = $this->rlp->encode($rawTxData); + $transactionType = $this->transactionType; + return $this->util->sha3(hex2bin($transactionType . $serializedTx)); + } +} \ No newline at end of file diff --git a/src/EIP2930Transaction.php b/src/EIP2930Transaction.php new file mode 100644 index 0000000..a54ae4e --- /dev/null +++ b/src/EIP2930Transaction.php @@ -0,0 +1,203 @@ + + * + * @author Peter Lai + * @license MIT + */ + +namespace Web3p\EthereumTx; + +use InvalidArgumentException; +use RuntimeException; +use Web3p\RLP\RLP; +use Elliptic\EC; +use Elliptic\EC\KeyPair; +use ArrayAccess; +use Web3p\EthereumUtil\Util; +use Web3p\EthereumTx\TypeTransaction; + +/** + * It's a instance for generating/serializing ethereum eip2930 transaction. + * + * ```php + * use Web3p\EthereumTx\EIP2930Transaction; + * + * // generate transaction instance with transaction parameters + * $transaction = new EIP2930Transaction([ + * 'nonce' => '0x01', + * 'from' => '0xb60e8dd61c5d32be8058bb8eb970870f07233155', + * 'to' => '0xd46e8dd67c5d32be8058bb8eb970870f07244567', + * 'gas' => '0x76c0', + * 'gasPrice' => '0x9184e72a000', + * 'value' => '0x9184e72a', + * 'chainId' => 1, // required + * 'accessList' => [], + * 'data' => '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675' + * ]); + * + * // generate transaction instance with hex encoded transaction + * $transaction = new EIP2930Transaction('0x01f86604158504a817c8008252089435353535353535353535353535353535353535358080c001a09753969d39f6a5109095d5082d67fc99a05fd66a339ba80934504ff79474e77aa07a907eb764b72b3088a331e7b97c2bad5fd43f1d574ddc80edeb022476454adb'); + * ``` + * + * ```php + * After generate transaction instance, you can sign transaction with your private key. + * + * $signedTransaction = $transaction->sign('your private key'); + * ``` + * + * Then you can send serialized transaction to ethereum through http rpc with web3.php. + * ```php + * $hashedTx = $transaction->serialize(); + * ``` + * + * @author Peter Lai + * @link https://www.web3p.xyz + * @filesource https://github.com/web3p/ethereum-tx + */ +class EIP2930Transaction extends TypeTransaction +{ + /** + * Attribute map for keeping order of transaction key/value + * + * @var array + */ + protected $attributeMap = [ + 'from' => [ + 'key' => -1 + ], + 'chainId' => [ + 'key' => 0 + ], + 'nonce' => [ + 'key' => 1, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'gasPrice' => [ + 'key' => 2, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'gasLimit' => [ + 'key' => 3, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'gas' => [ + 'key' => 3, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'to' => [ + 'key' => 4, + 'length' => 20, + 'allowZero' => true, + ], + 'value' => [ + 'key' => 5, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'data' => [ + 'key' => 6, + 'allowLess' => true, + 'allowZero' => true + ], + 'accessList' => [ + 'key' => 7, + 'allowLess' => true, + 'allowZero' => true, + 'allowArray' => true + ], + 'v' => [ + 'key' => 8, + 'allowZero' => true + ], + 'r' => [ + 'key' => 9, + 'length' => 32, + 'allowZero' => true + ], + 's' => [ + 'key' => 10, + 'length' => 32, + 'allowZero' => true + ] + ]; + + /** + * Transaction type + * + * @var string + */ + protected $transactionType = '01'; + + /** + * construct + * + * @param array|string $txData + * @return void + */ + public function __construct($txData=[]) + { + parent::__construct($txData); + } + + /** + * RLP serialize the ethereum transaction. + * + * @return string hex encoded of the serialized ethereum transaction + */ + public function serialize() + { + // sort tx data + if (ksort($this->txData) !== true) { + throw new RuntimeException('Cannot sort tx data by keys.'); + } + $txData = array_fill(0, 11, ''); + foreach ($this->txData as $key => $data) { + if ($key >= 0) { + $txData[$key] = $data; + } + } + $transactionType = $this->transactionType; + return $transactionType . $this->rlp->encode($txData); + } + + /** + * Return hash of the ethereum transaction with/without signature. + * + * @param bool $includeSignature hash with signature + * @return string hex encoded hash of the ethereum transaction + */ + public function hash(bool $includeSignature=false) + { + // sort tx data + if (ksort($this->txData) !== true) { + throw new RuntimeException('Cannot sort tx data by keys.'); + } + if ($includeSignature) { + $length = 11; + } else { + $length = 8; + } + $rawTxData = array_fill(0, $length, ''); + for ($key = 0; $key < $length; $key++) { + if (isset($this->txData[$key])) { + $rawTxData[$key] = $this->txData[$key]; + } + } + $serializedTx = $this->rlp->encode($rawTxData); + $transactionType = $this->transactionType; + return $this->util->sha3(hex2bin($transactionType . $serializedTx)); + } +} \ No newline at end of file diff --git a/src/Transaction.php b/src/Transaction.php index bca994d..f7dfefa 100644 --- a/src/Transaction.php +++ b/src/Transaction.php @@ -343,7 +343,7 @@ public function getTxData() /** * RLP serialize the ethereum transaction. * - * @return \Web3p\RLP\RLP\Buffer serialized ethereum transaction + * @return string hex encoded of the serialized ethereum transaction */ public function serialize() { diff --git a/src/TypeTransaction.php b/src/TypeTransaction.php new file mode 100755 index 0000000..a64d626 --- /dev/null +++ b/src/TypeTransaction.php @@ -0,0 +1,476 @@ + + * + * @author Peter Lai + * @license MIT + */ + +namespace Web3p\EthereumTx; + +use InvalidArgumentException; +use RuntimeException; +use Web3p\RLP\RLP; +use Elliptic\EC; +use Elliptic\EC\KeyPair; +use ArrayAccess; +use Web3p\EthereumUtil\Util; + +/** + * It's a base transaction for generating/serializing ethereum type transaction (EIP1559/EIP2930). + * Only use this class to generate new type transaction + * + * @author Peter Lai + * @link https://www.web3p.xyz + * @filesource https://github.com/web3p/ethereum-tx + */ +class TypeTransaction implements ArrayAccess +{ + /** + * Attribute map for keeping order of transaction key/value + * + * @var array + */ + protected $attributeMap = [ + 'from' => [ + 'key' => -1 + ], + 'chainId' => [ + 'key' => 0 + ], + 'nonce' => [ + 'key' => 1, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'gasPrice' => [ + 'key' => 2, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'gasLimit' => [ + 'key' => 3, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'gas' => [ + 'key' => 3, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'to' => [ + 'key' => 4, + 'length' => 20, + 'allowZero' => true, + ], + 'value' => [ + 'key' => 5, + 'length' => 32, + 'allowLess' => true, + 'allowZero' => false + ], + 'data' => [ + 'key' => 6, + 'allowLess' => true, + 'allowZero' => true + ], + 'v' => [ + 'key' => 7, + 'allowZero' => true + ], + 'r' => [ + 'key' => 8, + 'length' => 32, + 'allowZero' => true + ], + 's' => [ + 'key' => 9, + 'length' => 32, + 'allowZero' => true + ] + ]; + + /** + * Raw transaction data + * + * @var array + */ + protected $txData = []; + + /** + * RLP encoding instance + * + * @var \Web3p\RLP\RLP + */ + protected $rlp; + + /** + * secp256k1 elliptic curve instance + * + * @var \Elliptic\EC + */ + protected $secp256k1; + + /** + * Private key instance + * + * @var \Elliptic\EC\KeyPair + */ + protected $privateKey; + + /** + * Ethereum util instance + * + * @var \Web3p\EthereumUtil\Util + */ + protected $util; + + /** + * Transaction type + * + * @var string + */ + protected $transactionType = '00'; + + /** + * construct + * + * @param array|string $txData + * @return void + */ + public function __construct($txData=[]) + { + $this->rlp = new RLP; + $this->secp256k1 = new EC('secp256k1'); + $this->util = new Util; + + if (is_array($txData)) { + foreach ($txData as $key => $data) { + $this->offsetSet($key, $data); + } + } elseif (is_string($txData)) { + $tx = []; + + if ($this->util->isHex($txData)) { + // check first byte + $txData = $this->util->stripZero($txData); + $firstByteStr = substr($txData, 0, 2); + $firstByte = hexdec($firstByteStr); + if ($this->isTransactionTypeValid($firstByte)) { + $txData = substr($txData, 2); + } + $txData = $this->rlp->decode($txData); + + foreach ($txData as $txKey => $data) { + if (is_int($txKey)) { + if (is_string($data) && strlen($data) > 0) { + $tx[$txKey] = '0x' . $data; + } else { + $tx[$txKey] = $data; + } + } + } + } + $this->txData = $tx; + } + } + + /** + * Return the value in the transaction with given key or return the protected property value if get(property_name} function is existed. + * + * @param string $name key or protected property name + * @return mixed + */ + public function __get(string $name) + { + $method = 'get' . ucfirst($name); + + if (method_exists($this, $method)) { + return call_user_func_array([$this, $method], []); + } + return $this->offsetGet($name); + } + + /** + * Set the value in the transaction with given key or return the protected value if set(property_name} function is existed. + * + * @param string $name key, eg: to + * @param mixed value + * @return void + */ + public function __set(string $name, $value) + { + $method = 'set' . ucfirst($name); + + if (method_exists($this, $method)) { + return call_user_func_array([$this, $method], [$value]); + } + return $this->offsetSet($name, $value); + } + + /** + * Return hash of the ethereum transaction without signature. + * + * @return string hex encoded of the transaction + */ + public function __toString() + { + return $this->hash(false); + } + + /** + * Set the value in the transaction with given key. + * + * @param string $offset key, eg: to + * @param string value + * @return void + */ + public function offsetSet($offset, $value) + { + $txKey = isset($this->attributeMap[$offset]) ? $this->attributeMap[$offset] : null; + + if (is_array($txKey)) { + if (is_array($value)) { + if (!isset($txKey['allowArray']) || (isset($txKey['allowArray']) && $txKey['allowArray'] === false)) { + throw new InvalidArgumentException($offset . ' should\'t be array.'); + } + if (!isset($txKey['allowLess']) || (isset($txKey['allowLess']) && $txKey['allowLess'] === false)) { + // check length + if (isset($txKey['length'])) { + if (count($value) > $txKey['length'] * 2) { + throw new InvalidArgumentException($offset . ' exceeds the length limit.'); + } + } + } + if (!isset($txKey['allowZero']) || (isset($txKey['allowZero']) && $txKey['allowZero'] === false)) { + // check zero + foreach ($value as $key => $v) { + $checkedV = $v ? (string) $v : ''; + if (preg_match('/^0*$/', $checkedV) === 1) { + // set value to empty string + $checkedV = ''; + $value[$key] = $checkedV; + } + } + } + } else { + $checkedValue = ($value) ? (string) $value : ''; + $isHex = $this->util->isHex($checkedValue); + $checkedValue = $this->util->stripZero($checkedValue); + + if (!isset($txKey['allowLess']) || (isset($txKey['allowLess']) && $txKey['allowLess'] === false)) { + // check length + if (isset($txKey['length'])) { + if ($isHex) { + if (strlen($checkedValue) > $txKey['length'] * 2) { + throw new InvalidArgumentException($offset . ' exceeds the length limit.'); + } + } else { + if (strlen($checkedValue) > $txKey['length']) { + throw new InvalidArgumentException($offset . ' exceeds the length limit.'); + } + } + } + } + if (!isset($txKey['allowZero']) || (isset($txKey['allowZero']) && $txKey['allowZero'] === false)) { + // check zero + if (preg_match('/^0*$/', $checkedValue) === 1) { + // set value to empty string + $value = ''; + } + } + } + $this->txData[$txKey['key']] = $value; + } + } + + /** + * Return whether the value is in the transaction with given key. + * + * @param string $offset key, eg: to + * @return bool + */ + public function offsetExists($offset) + { + $txKey = isset($this->attributeMap[$offset]) ? $this->attributeMap[$offset] : null; + + if (is_array($txKey)) { + return isset($this->txData[$txKey['key']]); + } + return false; + } + + /** + * Unset the value in the transaction with given key. + * + * @param string $offset key, eg: to + * @return void + */ + public function offsetUnset($offset) + { + $txKey = isset($this->attributeMap[$offset]) ? $this->attributeMap[$offset] : null; + + if (is_array($txKey) && isset($this->txData[$txKey['key']])) { + unset($this->txData[$txKey['key']]); + } + } + + /** + * Return the value in the transaction with given key. + * + * @param string $offset key, eg: to + * @return mixed value of the transaction + */ + public function offsetGet($offset) + { + $txKey = isset($this->attributeMap[$offset]) ? $this->attributeMap[$offset] : null; + + if (is_array($txKey) && isset($this->txData[$txKey['key']])) { + return $this->txData[$txKey['key']]; + } + return null; + } + + /** + * Return raw ethereum transaction data. + * + * @return array raw ethereum transaction data + */ + public function getTxData() + { + return $this->txData; + } + + /** + * Return whether transaction type is valid (0x0 <= $transactionType <= 0x7f). + * + * @param integer $transactionType + * @return boolean is transaction valid + */ + protected function isTransactionTypeValid(int $transactionType) + { + return $transactionType >= 0 && $transactionType <= 127; + } + + /** + * RLP serialize the ethereum transaction. + * + * @return string hex encoded of the serialized ethereum transaction + */ + public function serialize() + { + // sort tx data + if (ksort($this->txData) !== true) { + throw new RuntimeException('Cannot sort tx data by keys.'); + } + $txData = array_fill(0, 10, ''); + foreach ($this->txData as $key => $data) { + if ($key >= 0) { + $txData[$key] = $data; + } + } + $transactionType = $this->transactionType; + return $transactionType . $this->rlp->encode($txData); + } + + /** + * Sign the transaction with given hex encoded private key. + * + * @param string $privateKey hex encoded private key + * @return string hex encoded signed ethereum transaction + */ + public function sign(string $privateKey) + { + if ($this->util->isHex($privateKey)) { + $privateKey = $this->util->stripZero($privateKey); + $ecPrivateKey = $this->secp256k1->keyFromPrivate($privateKey, 'hex'); + } else { + throw new InvalidArgumentException('Private key should be hex encoded string'); + } + $txHash = $this->hash(); + $signature = $ecPrivateKey->sign($txHash, [ + 'canonical' => true + ]); + $r = $signature->r; + $s = $signature->s; + $v = $signature->recoveryParam; + + $this->offsetSet('r', '0x' . $r->toString(16)); + $this->offsetSet('s', '0x' . $s->toString(16)); + $this->offsetSet('v', $v); + $this->privateKey = $ecPrivateKey; + + return $this->serialize(); + } + + /** + * Return hash of the ethereum transaction with/without signature. + * + * @param bool $includeSignature hash with signature + * @return string hex encoded hash of the ethereum transaction + */ + public function hash(bool $includeSignature=false) + { + // sort tx data + if (ksort($this->txData) !== true) { + throw new RuntimeException('Cannot sort tx data by keys.'); + } + if ($includeSignature) { + $length = 10; + } else { + $length = 7; + } + $rawTxData = array_fill(0, $length, ''); + for ($key = 0; $key < $length; $key++) { + if (isset($this->txData[$key])) { + $rawTxData[$key] = $this->txData[$key]; + } + } + $serializedTx = $this->rlp->encode($rawTxData); + $transactionType = $this->transactionType; + return $this->util->sha3(hex2bin($transactionType . $serializedTx)); + } + + /** + * Recover from address with given signature (r, s, v) if didn't set from. + * + * @return string hex encoded ethereum address + */ + public function getFromAddress() + { + $from = $this->offsetGet('from'); + + if ($from) { + return $from; + } + if (!isset($this->privateKey) || !($this->privateKey instanceof KeyPair)) { + // recover from hash + $r = $this->offsetGet('r'); + $s = $this->offsetGet('s'); + $v = $this->offsetGet('v'); + + if (!$r || !$s) { + throw new RuntimeException('Invalid signature r and s.'); + } + $txHash = $this->hash(); + $publicKey = $this->secp256k1->recoverPubKey($txHash, [ + 'r' => $r, + 's' => $s + ], $v); + $publicKey = $publicKey->encode('hex'); + } else { + $publicKey = $this->privateKey->getPublic(false, 'hex'); + } + $from = '0x' . substr($this->util->sha3(substr(hex2bin($publicKey), 1)), 24); + + $this->offsetSet('from', $from); + return $from; + } +} \ No newline at end of file diff --git a/test/TestCase.php b/test/TestCase.php index d1acdac..524377e 100644 --- a/test/TestCase.php +++ b/test/TestCase.php @@ -26,7 +26,7 @@ class TestCase extends BaseTestCase * * @return void */ - public function setUp() + public function setUp(): void { $this->rlp = new RLP; } @@ -36,5 +36,5 @@ public function setUp() * * @return void */ - public function tearDown() {} + public function tearDown(): void {} } \ No newline at end of file diff --git a/test/unit/TransactionTest.php b/test/unit/TransactionTest.php index 197a7e8..ace9e81 100644 --- a/test/unit/TransactionTest.php +++ b/test/unit/TransactionTest.php @@ -4,6 +4,8 @@ use Test\TestCase; use Web3p\EthereumTx\Transaction; +use Web3p\EthereumTx\EIP2930Transaction; +use Web3p\EthereumTx\EIP1559Transaction; class TransactionTest extends TestCase { @@ -347,4 +349,58 @@ public function testIssue26() $this->assertEquals($transaction->txData, []); } } + + /** + * testEIP2930 + * see: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2930.md + * + * @return void + */ + public function testEIP2930() + { + $transaction = new EIP2930Transaction([ + 'nonce' => '0x15', + 'to' => '0x3535353535353535353535353535353535353535', + 'gas' => '0x5208', + 'gasPrice' => '0x4a817c800', + 'value' => '0x0', + 'chainId' => 4, + 'accessList' => [ + ], + 'data' => '' + ]); + $this->assertEquals('01f86604158504a817c8008252089435353535353535353535353535353535353535358080c001a09753969d39f6a5109095d5082d67fc99a05fd66a339ba80934504ff79474e77aa07a907eb764b72b3088a331e7b97c2bad5fd43f1d574ddc80edeb022476454adb', $transaction->sign('0x4646464646464646464646464646464646464646464646464646464646464646')); + + $transaction = new EIP2930Transaction('0x01f86604158504a817c8008252089435353535353535353535353535353535353535358080c001a09753969d39f6a5109095d5082d67fc99a05fd66a339ba80934504ff79474e77aa07a907eb764b72b3088a331e7b97c2bad5fd43f1d574ddc80edeb022476454adb'); + $this->assertEquals('01f86604158504a817c8008252089435353535353535353535353535353535353535358080c001a09753969d39f6a5109095d5082d67fc99a05fd66a339ba80934504ff79474e77aa07a907eb764b72b3088a331e7b97c2bad5fd43f1d574ddc80edeb022476454adb', $transaction->serialize()); + $this->assertEquals('01f86604158504a817c8008252089435353535353535353535353535353535353535358080c001a09753969d39f6a5109095d5082d67fc99a05fd66a339ba80934504ff79474e77aa07a907eb764b72b3088a331e7b97c2bad5fd43f1d574ddc80edeb022476454adb', $transaction->sign('0x4646464646464646464646464646464646464646464646464646464646464646')); + } + + /** + * testEIP1559 + * see: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md + * + * @return void + */ + public function testEIP1559() + { + $transaction = new EIP1559Transaction([ + 'nonce' => '0x15', + 'to' => '0x3535353535353535353535353535353535353535', + 'gas' => '0x5208', + 'maxPriorityFeePerGas' => '0x4a817c800', + 'maxFeePerGas' => '0x4a817c800', + 'value' => '0x0', + 'chainId' => 4, + 'accessList' => [ + ], + 'data' => '' + ]); + var_dump($transaction->hash()); + $this->assertEquals('02f86c04158504a817c8008504a817c8008252089435353535353535353535353535353535353535358080c080a03fd48c8a173e9669c33cb5271f03b1af4f030dc8315be8ec9442b7fbdde893c8a010af381dab1df3e7012a3c8421d65a810859a5dd9d58991ad7c07f12d0c651c7', $transaction->sign('0x4646464646464646464646464646464646464646464646464646464646464646')); + + $transaction = new EIP1559Transaction('0x02f86c04158504a817c8008504a817c8008252089435353535353535353535353535353535353535358080c080a03fd48c8a173e9669c33cb5271f03b1af4f030dc8315be8ec9442b7fbdde893c8a010af381dab1df3e7012a3c8421d65a810859a5dd9d58991ad7c07f12d0c651c7'); + $this->assertEquals('02f86c04158504a817c8008504a817c8008252089435353535353535353535353535353535353535358080c080a03fd48c8a173e9669c33cb5271f03b1af4f030dc8315be8ec9442b7fbdde893c8a010af381dab1df3e7012a3c8421d65a810859a5dd9d58991ad7c07f12d0c651c7', $transaction->serialize()); + $this->assertEquals('02f86c04158504a817c8008504a817c8008252089435353535353535353535353535353535353535358080c080a03fd48c8a173e9669c33cb5271f03b1af4f030dc8315be8ec9442b7fbdde893c8a010af381dab1df3e7012a3c8421d65a810859a5dd9d58991ad7c07f12d0c651c7', $transaction->sign('0x4646464646464646464646464646464646464646464646464646464646464646')); + } }