diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 7701ad57..276a9a3d 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -7,7 +7,7 @@ jobs: name: Build and test web3.php with ${{ matrix.php-version }} strategy: matrix: - php-version: ["7.3", "7.4", "8.0"] + php-version: ["7.3", "7.4", "8.0", "8.1", "8.2"] runs-on: ubuntu-latest diff --git a/composer.json b/composer.json index e03dcef0..b58bfda9 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,12 @@ "PHP": "^7.2|^8.0", "kornrunner/keccak": "~1.0", "phpseclib/phpseclib": "~2.0.30", - "ext-mbstring": "*" + "ext-mbstring": "*", + "react/http": "^1.6.0", + "react/async": "^4.0.0|^3.1.0", + "react/promise": "^2.9.0", + "react/event-loop": "^1.2", + "react/socket": "^1.13" }, "require-dev": { "phpunit/phpunit": "~8.0|~9.0" diff --git a/docker/ganache/Dockerfile b/docker/ganache/Dockerfile index 17726d6d..5f99bb9f 100644 --- a/docker/ganache/Dockerfile +++ b/docker/ganache/Dockerfile @@ -1,4 +1,4 @@ -FROM node:9.11.1-alpine +FROM node:12.11.1-alpine MAINTAINER Peter Lai diff --git a/docker/php/Dockerfile-71 b/docker/php/Dockerfile-81 similarity index 93% rename from docker/php/Dockerfile-71 rename to docker/php/Dockerfile-81 index 0df7a269..ded44c5b 100644 --- a/docker/php/Dockerfile-71 +++ b/docker/php/Dockerfile-81 @@ -1,4 +1,4 @@ -FROM php:7.1-alpine +FROM php:8.1-alpine MAINTAINER Peter Lai @@ -20,4 +20,4 @@ RUN php composer-setup.php && \ php composer-setup.php --install-dir=/usr/bin --filename=composer && \ php -r "unlink('composer-setup.php');" -WORKDIR /app \ No newline at end of file +WORKDIR /app diff --git a/docker/php/Dockerfile-82 b/docker/php/Dockerfile-82 new file mode 100644 index 00000000..4483817a --- /dev/null +++ b/docker/php/Dockerfile-82 @@ -0,0 +1,23 @@ +FROM php:8.2-alpine + +MAINTAINER Peter Lai + +COPY composer-setup.php composer-setup.php +# COPY php.ini-production $PHP_INI_DIR/php.ini + +RUN apk update && \ + apk add git + +# Install gmp +Run apk add gmp-dev && \ + docker-php-ext-install gmp + +# Install nodejs +# Run apk add --update nodejs nodejs-npm + +# Install composer +RUN php composer-setup.php && \ + php composer-setup.php --install-dir=/usr/bin --filename=composer && \ + php -r "unlink('composer-setup.php');" + +WORKDIR /app diff --git a/src/Contract.php b/src/Contract.php index 87ad1fc4..ba5c51ea 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -441,7 +441,7 @@ public function abi($abi) * Deploy a contruct with params. * * @param mixed - * @return void + * @return void|\React\Promise\PromiseInterface */ public function new() { @@ -469,7 +469,7 @@ public function new() } $transaction['data'] = '0x' . $this->bytecode . Utils::stripZero($data); - $this->eth->sendTransaction($transaction, function ($err, $transaction) use ($callback){ + return $this->eth->sendTransaction($transaction, function ($err, $transaction) use ($callback){ if ($err !== null) { return call_user_func($callback, $err, null); } @@ -483,7 +483,7 @@ public function new() * Send function method. * * @param mixed - * @return void + * @return void|\React\Promise\PromiseInterface */ public function send() { @@ -564,7 +564,7 @@ public function send() $transaction['to'] = $this->toAddress; $transaction['data'] = $functionSignature . Utils::stripZero($data); - $this->eth->sendTransaction($transaction, function ($err, $transaction) use ($callback){ + return $this->eth->sendTransaction($transaction, function ($err, $transaction) use ($callback){ if ($err !== null) { return call_user_func($callback, $err, null); } @@ -578,7 +578,7 @@ public function send() * Call function method. * * @param mixed - * @return void + * @return void|\React\Promise\PromiseInterface */ public function call() { @@ -658,7 +658,7 @@ public function call() $transaction['to'] = $this->toAddress; $transaction['data'] = $functionSignature . Utils::stripZero($data); - $this->eth->call($transaction, $defaultBlock, function ($err, $transaction) use ($callback, $function){ + return $this->eth->call($transaction, $defaultBlock, function ($err, $transaction) use ($callback, $function){ if ($err !== null) { return call_user_func($callback, $err, null); } @@ -674,7 +674,7 @@ public function call() * Estimate function gas. * * @param mixed - * @return void + * @return void|\React\Promise\PromiseInterface */ public function estimateGas() { @@ -778,7 +778,7 @@ public function estimateGas() $transaction['data'] = $functionSignature . Utils::stripZero($data); } - $this->eth->estimateGas($transaction, function ($err, $gas) use ($callback) { + return $this->eth->estimateGas($transaction, function ($err, $gas) use ($callback) { if ($err !== null) { return call_user_func($callback, $err, null); } diff --git a/src/Eth.php b/src/Eth.php index 78d97989..022aaa7b 100644 --- a/src/Eth.php +++ b/src/Eth.php @@ -66,7 +66,7 @@ public function __construct($provider) * * @param string $name * @param array $arguments - * @return void + * @return void|\React\Promise\PromiseInterface */ public function __call($name, $arguments) { @@ -102,7 +102,7 @@ public function __call($name, $arguments) if ($methodObject->validate($arguments)) { $inputs = $methodObject->transform($arguments, $methodObject->inputFormatters); $methodObject->arguments = $inputs; - $this->provider->send($methodObject, $callback); + return $this->provider->send($methodObject, $callback); } } } diff --git a/src/Methods/Eth/CompileLLL.php b/src/Methods/Eth/CompileLLL.php deleted file mode 100644 index 69d33f6a..00000000 --- a/src/Methods/Eth/CompileLLL.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * @author Peter Lai - * @license MIT - */ - -namespace Web3\Methods\Eth; - -use InvalidArgumentException; -use Web3\Methods\EthMethod; -use Web3\Validators\StringValidator; -use Web3\Formatters\StringFormatter; - -class CompileLLL extends EthMethod -{ - /** - * validators - * - * @var array - */ - protected $validators = [ - StringValidator::class - ]; - - /** - * inputFormatters - * - * @var array - */ - protected $inputFormatters = [ - StringFormatter::class - ]; - - /** - * outputFormatters - * - * @var array - */ - protected $outputFormatters = []; - - /** - * defaultValues - * - * @var array - */ - protected $defaultValues = []; - - /** - * construct - * - * @param string $method - * @param array $arguments - * @return void - */ - // public function __construct($method='', $arguments=[]) - // { - // parent::__construct($method, $arguments); - // } -} \ No newline at end of file diff --git a/src/Methods/Eth/CompileSerpent.php b/src/Methods/Eth/CompileSerpent.php deleted file mode 100644 index 39b105d2..00000000 --- a/src/Methods/Eth/CompileSerpent.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * @author Peter Lai - * @license MIT - */ - -namespace Web3\Methods\Eth; - -use InvalidArgumentException; -use Web3\Methods\EthMethod; -use Web3\Validators\StringValidator; -use Web3\Formatters\StringFormatter; - -class CompileSerpent extends EthMethod -{ - /** - * validators - * - * @var array - */ - protected $validators = [ - StringValidator::class - ]; - - /** - * inputFormatters - * - * @var array - */ - protected $inputFormatters = [ - StringFormatter::class - ]; - - /** - * outputFormatters - * - * @var array - */ - protected $outputFormatters = []; - - /** - * defaultValues - * - * @var array - */ - protected $defaultValues = []; - - /** - * construct - * - * @param string $method - * @param array $arguments - * @return void - */ - // public function __construct($method='', $arguments=[]) - // { - // parent::__construct($method, $arguments); - // } -} \ No newline at end of file diff --git a/src/Methods/Eth/CompileSolidity.php b/src/Methods/Eth/CompileSolidity.php deleted file mode 100644 index 05de3c0d..00000000 --- a/src/Methods/Eth/CompileSolidity.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * @author Peter Lai - * @license MIT - */ - -namespace Web3\Methods\Eth; - -use InvalidArgumentException; -use Web3\Methods\EthMethod; -use Web3\Validators\StringValidator; -use Web3\Formatters\StringFormatter; - -class CompileSolidity extends EthMethod -{ - /** - * validators - * - * @var array - */ - protected $validators = [ - StringValidator::class - ]; - - /** - * inputFormatters - * - * @var array - */ - protected $inputFormatters = [ - StringFormatter::class - ]; - - /** - * outputFormatters - * - * @var array - */ - protected $outputFormatters = []; - - /** - * defaultValues - * - * @var array - */ - protected $defaultValues = []; - - /** - * construct - * - * @param string $method - * @param array $arguments - * @return void - */ - // public function __construct($method='', $arguments=[]) - // { - // parent::__construct($method, $arguments); - // } -} \ No newline at end of file diff --git a/src/Net.php b/src/Net.php index bb06615d..adae1047 100644 --- a/src/Net.php +++ b/src/Net.php @@ -66,7 +66,7 @@ public function __construct($provider) * * @param string $name * @param array $arguments - * @return void + * @return void|\React\Promise\PromiseInterface */ public function __call($name, $arguments) { @@ -102,7 +102,7 @@ public function __call($name, $arguments) if ($methodObject->validate($arguments)) { $inputs = $methodObject->transform($arguments, $methodObject->inputFormatters); $methodObject->arguments = $inputs; - $this->provider->send($methodObject, $callback); + return $this->provider->send($methodObject, $callback); } } } diff --git a/src/Personal.php b/src/Personal.php index 75aa200c..8b479776 100644 --- a/src/Personal.php +++ b/src/Personal.php @@ -66,7 +66,7 @@ public function __construct($provider) * * @param string $name * @param array $arguments - * @return void + * @return void|\React\Promise\PromiseInterface */ public function __call($name, $arguments) { @@ -102,7 +102,7 @@ public function __call($name, $arguments) if ($methodObject->validate($arguments)) { $inputs = $methodObject->transform($arguments, $methodObject->inputFormatters); $methodObject->arguments = $inputs; - $this->provider->send($methodObject, $callback); + return $this->provider->send($methodObject, $callback); } } } diff --git a/src/Providers/HttpProvider.php b/src/Providers/HttpProvider.php index b99de556..05e4f366 100644 --- a/src/Providers/HttpProvider.php +++ b/src/Providers/HttpProvider.php @@ -59,7 +59,7 @@ public function send($method, $callback) return call_user_func($callback, null, $res); }; - $this->requestManager->sendPayload($payload, $proxy); + return $this->requestManager->sendPayload($payload, $proxy); } else { $this->methods[] = $method; $this->batch[] = $payload; @@ -108,8 +108,9 @@ public function execute($callback) } return call_user_func($callback, null, $res); }; - $this->requestManager->sendPayload('[' . implode(',', $this->batch) . ']', $proxy); - $this->methods[] = []; + $r = $this->requestManager->sendPayload('[' . implode(',', $this->batch) . ']', $proxy); + $this->methods = []; $this->batch = []; + return $r; } } \ No newline at end of file diff --git a/src/RequestManagers/HttpAsyncRequestManager.php b/src/RequestManagers/HttpAsyncRequestManager.php new file mode 100644 index 00000000..0c655999 --- /dev/null +++ b/src/RequestManagers/HttpAsyncRequestManager.php @@ -0,0 +1,125 @@ + + * + * @author Peter Lai + * @license MIT + */ + +namespace Web3\RequestManagers; + +use InvalidArgumentException; +use Psr\Http\Message\StreamInterface; +use RuntimeException as RPCException; +use Psr\Http\Message\ResponseInterface; +use React; +use React\Async; +use React\EventLoop\Loop; +use React\Http\Browser; +use React\Socket\Connector; +use Web3\RequestManagers\RequestManager; +use Web3\RequestManagers\IRequestManager; + +class HttpAsyncRequestManager extends RequestManager implements IRequestManager +{ + /** + * client + * + * @var \React\Http\Browser + */ + protected $client; + + /** + * construct + * + * @param string $host + * @param int $timeout + * @return void + */ + public function __construct($host, $timeout = 1) + { + parent::__construct($host, $timeout); + $connector = new Connector([ + 'timeout' => $timeout, + ], Loop::get()); + $this->client = (new Browser($connector, Loop::get()))->withRejectErrorResponse(false); + } + + /** + * sendPayload + * + * @param string $payload + * @param callable $callback + * @return void + */ + public function sendPayload($payload, $callback) + { + if (!is_string($payload)) { + throw new \InvalidArgumentException('Payload must be string.'); + } + + $host = $this->host; + $request = function () use ($host, $payload, $callback) { + try { + $headers = [ + 'content-type' => 'application/json' + ]; + $res = Async\await($this->client->request('POST', $host, $headers, $payload)); + /** + * @var StreamInterface $stream ; + */ + $stream = $res->getBody(); + $json = json_decode($stream); + $stream->close(); + + if (JSON_ERROR_NONE !== json_last_error()) { + call_user_func($callback, new InvalidArgumentException('json_decode error: ' . json_last_error_msg()), null); + } + if (is_array($json)) { + // batch results + $results = []; + $errors = []; + + foreach ($json as $result) { + if (property_exists($result,'result')) { + $results[] = $result->result; + } else { + if (isset($json->error)) { + $error = $json->error; + $errors[] = new RPCException(mb_ereg_replace('Error: ', '', $error->message), $error->code); + } else { + $results[] = null; + } + } + } + if (count($errors) > 0) { + call_user_func($callback, $errors, $results); + } else { + call_user_func($callback, null, $results); + } + } elseif (property_exists($json,'result')) { + call_user_func($callback, null, $json->result); + } else { + if (isset($json->error)) { + $error = $json->error; + + call_user_func($callback, new RPCException(mb_ereg_replace('Error: ', '', $error->message), $error->code), null); + } else { + call_user_func($callback, new RPCException('Something wrong happened.'), null); + } + } + } catch (Exception $err) { + call_user_func($callback, $err, null); + } + }; + + if (function_exists('React\\Async\\async')) { + $request = Async\async($request); + } + + return Async\coroutine($request); + } +} diff --git a/src/Shh.php b/src/Shh.php index bc438e4b..ed38738b 100644 --- a/src/Shh.php +++ b/src/Shh.php @@ -67,7 +67,7 @@ public function __construct($provider) * * @param string $name * @param array $arguments - * @return void + * @return void|\React\Promise\PromiseInterface */ public function __call($name, $arguments) { @@ -103,7 +103,7 @@ public function __call($name, $arguments) if ($methodObject->validate($arguments)) { $inputs = $methodObject->transform($arguments, $methodObject->inputFormatters); $methodObject->arguments = $inputs; - $this->provider->send($methodObject, $callback); + return $this->provider->send($methodObject, $callback); } } } diff --git a/src/Web3.php b/src/Web3.php index 35a2cded..4e489f77 100644 --- a/src/Web3.php +++ b/src/Web3.php @@ -142,7 +142,7 @@ public function __call($name, $arguments) if ($methodObject->validate($arguments)) { $inputs = $methodObject->transform($arguments, $methodObject->inputFormatters); $methodObject->arguments = $inputs; - $this->provider->send($methodObject, $callback); + return $this->provider->send($methodObject, $callback); } } } diff --git a/test/TestCase.php b/test/TestCase.php index 373a8b31..18d298e6 100644 --- a/test/TestCase.php +++ b/test/TestCase.php @@ -4,6 +4,8 @@ use \PHPUnit\Framework\TestCase as BaseTestCase; use Web3\Web3; +use Web3\RequestManagers\HttpAsyncRequestManager; +use Web3\Providers\HttpProvider; class TestCase extends BaseTestCase { @@ -15,11 +17,11 @@ class TestCase extends BaseTestCase protected $web3; /** - * testRinkebyHost + * testHost2 * * @var string */ - protected $testRinkebyHost = 'https://rinkeby.infura.io/vuethexplore'; + protected $testHost2 = 'https://eth-mainnet.g.alchemy.com/v2/notavalidkey'; /** * testHost @@ -35,6 +37,13 @@ class TestCase extends BaseTestCase */ protected $coinbase; + /** + * asyncHttpProvider + * + * @var \Web3\Providers\HttpProvider + */ + protected $asyncHttpProvider; + /** * setUp */ @@ -43,6 +52,10 @@ public function setUp(): void $web3 = new Web3($this->testHost); $this->web3 = $web3; + $asyncRequestManager = new HttpAsyncRequestManager($this->testHost); + $asyncHttpProvider = new HttpProvider($asyncRequestManager); + $this->asyncHttpProvider = $asyncHttpProvider; + $web3->eth->coinbase(function ($err, $coinbase) { if ($err !== null) { return $this->fail($err->getMessage()); diff --git a/test/unit/ContractTest.php b/test/unit/ContractTest.php index 5061abc5..3d72bde4 100644 --- a/test/unit/ContractTest.php +++ b/test/unit/ContractTest.php @@ -21,6 +21,13 @@ class ContractTest extends TestCase */ protected $contract; + /** + * async contract + * + * @var \Web3\Contract + */ + protected $asyncContract; + /** * testAbi * GameToken abi from https://github.com/sc0Vu/GameToken @@ -425,6 +432,7 @@ public function setUp(): void } } }); + $this->asyncContract = new Contract($this->asyncHttpProvider, $this->testAbi); } /** @@ -708,7 +716,7 @@ public function testEstimateGas() ], function ($err, $result) use ($contract) { if ($err !== null) { // infura api gg - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } if (isset($result)) { echo "\nEstimate gas: " . $result->toString() . "\n"; @@ -1514,4 +1522,117 @@ public function testIssue134() } }); } + + /** + * testAsync + * + * @return void + */ + public function testAsync() + { + $contract = $this->contract; + + if (!isset($this->accounts[0])) { + $fromAccount = '0x407d73d8a49eeb85d32cf465507dd71d507100c1'; + } else { + $fromAccount = $this->accounts[0]; + } + if (!isset($this->accounts[1])) { + $toAccount = '0x407d73d8a49eeb85d32cf465507dd71d507100c2'; + } else { + $toAccount = $this->accounts[1]; + } + $contract->bytecode($this->testBytecode)->new(1000000, 'Game Token', 1, 'GT', [ + 'from' => $fromAccount, + 'gas' => '0x200b20' + ], function ($err, $result) use ($contract) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + if ($result) { + echo "\nTransaction has made:) id: " . $result . "\n"; + } + $transactionId = $result; + $this->assertTrue((preg_match('/^0x[a-f0-9]{64}$/', $transactionId) === 1)); + + $contract->eth->getTransactionReceipt($transactionId, function ($err, $transaction) { + if ($err !== null) { + return $this->fail($err); + } + if ($transaction) { + $this->contractAddress = $transaction->contractAddress; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; + } + }); + }); + + if (!isset($this->contractAddress)) { + $this->contractAddress = '0x407d73d8a49eeb85d32cf465507dd71d507100c2'; + } + $asyncContract = $this->asyncContract; + $promise1 = $asyncContract->at($this->contractAddress)->send('transfer', $toAccount, 16, [ + 'from' => $fromAccount, + 'gas' => '0x200b20' + ], function ($err, $result) use ($contract, $fromAccount, $toAccount, $asyncContract) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + if ($result) { + echo "\nTransaction has made:) id: " . $result . "\n"; + } + $transactionId = $result; + $this->assertTrue((preg_match('/^0x[a-f0-9]{64}$/', $transactionId) === 1)); + + $promise = $asyncContract->eth->getTransactionReceipt($transactionId, function ($err, $transaction) use ($fromAccount, $toAccount, $contract) { + if ($err !== null) { + return $this->fail($err); + } + if ($transaction) { + $topics = $transaction->logs[0]->topics; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; + + // validate topics + $this->assertEquals($contract->ethabi->encodeEventSignature($this->contract->events['Transfer']), $topics[0]); + $this->assertEquals('0x' . IntegerFormatter::format($fromAccount), $topics[1]); + $this->assertEquals('0x' . IntegerFormatter::format($toAccount), $topics[2]); + } + }); + $this->assertTrue($promise instanceof \React\Promise\PromiseInterface); + \React\Async\await($promise); + }); + $this->assertTrue($promise1 instanceof \React\Promise\PromiseInterface); + + $promise2 = $asyncContract->at($this->contractAddress)->call('balanceOf', $fromAccount, [ + 'from' => $fromAccount + ], function ($err, $result) use ($contract) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + if (isset($result)) { + // $bn = Utils::toBn($result); + // $this->assertEquals($bn->toString(), '10000', 'Balance should be 10000.'); + $this->assertTrue($result !== null); + } + }); + + $this->assertTrue($promise2 instanceof \React\Promise\PromiseInterface); + + $promise3 = $asyncContract->bytecode($this->testBytecode)->estimateGas('balanceOf', $toAccount, [ + 'from' => $fromAccount, + 'gas' => '0x200b20' + ], function ($err, $result) use ($contract) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + if (isset($result)) { + echo "\nEstimate gas: " . $result->toString() . "\n"; + $this->assertTrue($result !== null); + } + }); + \React\Async\await(\React\Async\parallel([ + function () use ($promise1) { return $promise1; }, + function () use ($promise2) { return $promise2; }, + function () use ($promise3) { return $promise3; }, + ])); + } } \ No newline at end of file diff --git a/test/unit/EthApiTest.php b/test/unit/EthApiTest.php index 2f58bc45..e1d2a4c2 100644 --- a/test/unit/EthApiTest.php +++ b/test/unit/EthApiTest.php @@ -227,7 +227,7 @@ public function testGetBlockTransactionCountByHash() $eth->getBlockTransactionCountByHash('0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238', function ($err, $transactionCount) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->assertEquals('Key not found in database', $err->getMessage()); } $this->assertTrue(is_numeric($transactionCount->toString())); }); @@ -244,7 +244,7 @@ public function testGetBlockTransactionCountByNumber() $eth->getBlockTransactionCountByNumber('0x0', function ($err, $transactionCount) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_numeric($transactionCount->toString())); }); @@ -261,7 +261,7 @@ public function testGetUncleCountByBlockHash() $eth->getUncleCountByBlockHash('0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238', function ($err, $uncleCount) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_numeric($uncleCount->toString())); }); @@ -278,7 +278,7 @@ public function testGetUncleCountByBlockNumber() $eth->getUncleCountByBlockNumber('0x0', function ($err, $uncleCount) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_numeric($uncleCount->toString())); }); @@ -295,7 +295,7 @@ public function testGetCode() $eth->getCode('0x407d73d8a49eeb85d32cf465507dd71d507100c1', function ($err, $code) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_string($code)); }); @@ -312,8 +312,7 @@ public function testSign() $eth->sign('0x407d73d8a49eeb85d32cf465507dd71d507100c1', '0xdeadbeaf', function ($err, $sign) { if ($err !== null) { - // infura banned us to sign message - return $this->assertTrue($err !== null); + return $this->assertEquals('cannot sign data; no private key', $err->getMessage()); } $this->assertTrue(is_string($sign)); }); @@ -337,8 +336,7 @@ public function testSendTransaction() 'data' => "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" ], function ($err, $transaction) { if ($err !== null) { - // infura banned us to send transaction - return $this->assertTrue($err !== null); + return $this->assertEquals('sender account not recognized', $err->getMessage()); } $this->assertTrue(is_string($transaction)); }); @@ -355,7 +353,7 @@ public function testSendRawTransaction() $eth->sendRawTransaction('0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675', function ($err, $transaction) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->assertEquals('invalid remainder', $err->getMessage()); } $this->assertTrue(is_string($transaction)); }); @@ -379,7 +377,7 @@ public function testCall() 'data' => "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" ], function ($err, $transaction) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_string($transaction)); }); @@ -403,7 +401,7 @@ public function testEstimateGas() 'data' => "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" ], function ($err, $gas) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_numeric($gas->toString())); }); @@ -420,7 +418,7 @@ public function testGetBlockByHash() $eth->getBlockByHash('0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238', false, function ($err, $block) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->assertEquals('Key not found in database', $err->getMessage()); } $this->assertTrue($block !== null); }); @@ -437,7 +435,7 @@ public function testGetBlockByNumber() $eth->getBlockByNumber('latest', false, function ($err, $block) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } // weird behavior, see https://github.com/web3p/web3.php/issues/16 $this->assertTrue($block !== null); @@ -455,7 +453,7 @@ public function testGetTransactionByHash() $eth->getTransactionByHash('0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238', function ($err, $transaction) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue($transaction == null); }); @@ -472,7 +470,7 @@ public function testGetTransactionByBlockHashAndIndex() $eth->getTransactionByBlockHashAndIndex('0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238', '0x0', function ($err, $transaction) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue($transaction == null); }); @@ -489,7 +487,7 @@ public function testGetTransactionByBlockNumberAndIndex() $eth->getTransactionByBlockNumberAndIndex('0xe8', '0x0', function ($err, $transaction) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->assertStringStartsWith('LevelUpArrayAdapter named \'blocks\' index out of range', $err->getMessage()); } $this->assertTrue($transaction !== null); }); @@ -506,7 +504,7 @@ public function testGetTransactionReceipt() $eth->getTransactionReceipt('0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238', function ($err, $transaction) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue($transaction == null); }); @@ -523,7 +521,7 @@ public function testGetUncleByBlockHashAndIndex() $eth->getUncleByBlockHashAndIndex('0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238', '0x0', function ($err, $uncle) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue($uncle !== null); }); @@ -540,63 +538,12 @@ public function testGetUncleByBlockNumberAndIndex() $eth->getUncleByBlockNumberAndIndex('0xe8', '0x0', function ($err, $uncle) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue($uncle !== null); }); } - /** - * testCompileSolidity - * - * @return void - */ - public function testCompileSolidity() - { - $eth = $this->eth; - - $eth->compileSolidity('contract test { function multiply(uint a) returns(uint d) { return a * 7; } }', function ($err, $compiled) { - if ($err !== null) { - return $this->assertTrue($err !== null); - } - $this->assertTrue(is_string($compiled)); - }); - } - - /** - * testCompileLLL - * - * @return void - */ - public function testCompileLLL() - { - $eth = $this->eth; - - $eth->compileLLL('(returnlll (suicide (caller)))', function ($err, $compiled) { - if ($err !== null) { - return $this->assertTrue($err !== null); - } - $this->assertTrue(is_string($compiled)); - }); - } - - /** - * testCompileSerpent - * - * @return void - */ - public function testCompileSerpent() - { - $eth = $this->eth; - - $eth->compileSerpent('\/* some serpent *\/', function ($err, $compiled) { - if ($err !== null) { - return $this->assertTrue($err !== null); - } - $this->assertTrue(is_string($compiled)); - }); - } - /** * testNewFilter * @@ -614,7 +561,7 @@ public function testNewFilter() ], function ($err, $filter) { if ($err !== null) { // infura banned us to new filter - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_string($filter)); }); @@ -632,7 +579,7 @@ public function testNewBlockFilter() $eth->newBlockFilter(function ($err, $filter) { if ($err !== null) { // infura banned us to new block filter - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_string($filter)); }); @@ -650,7 +597,7 @@ public function testNewPendingTransactionFilter() $eth->newPendingTransactionFilter(function ($err, $filter) { if ($err !== null) { // infura banned us to new pending transaction filter - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_string($filter)); }); @@ -668,7 +615,7 @@ public function testUninstallFilter() $eth->uninstallFilter('0x01', function ($err, $filter) { if ($err !== null) { // infura banned us to uninstall filter - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_bool($filter)); }); @@ -686,7 +633,7 @@ public function testGetFilterChanges() $eth->getFilterChanges('0x01', function ($err, $changes) { if ($err !== null) { // infura banned us to get filter changes - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_array($changes)); }); @@ -704,7 +651,7 @@ public function testGetFilterLogs() $eth->getFilterLogs('0x01', function ($err, $logs) { if ($err !== null) { // infura banned us to get filter logs - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_array($logs)); }); @@ -726,68 +673,73 @@ public function testGetLogs() 'topics' => ['0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b', null, ['0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b', '0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc']] ], function ($err, $logs) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_array($logs)); }); } /** - * testGetWork + * testSubmitWork * * @return void */ - public function testGetWork() + public function testSubmitWork() { $eth = $this->eth; - $eth->getWork(function ($err, $work) { + $eth->submitWork( + '0x0000000000000001', + '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', + '0xD1FE5700000000000000000000000000D1FE5700000000000000000000000000' + , function ($err, $work) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } - $this->assertTrue(is_array($work)); + $this->assertTrue(is_bool($work)); }); } /** - * testSubmitWork + * testSubmitHashrate * * @return void */ - public function testSubmitWork() + public function testSubmitHashrate() { $eth = $this->eth; - $eth->submitWork( - '0x0000000000000001', + $eth->submitHashrate( '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', '0xD1FE5700000000000000000000000000D1FE5700000000000000000000000000' , function ($err, $work) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_bool($work)); }); } /** - * testSubmitHashrate + * testGetBlockByNumberAsync * * @return void */ - public function testSubmitHashrate() + public function testGetBlockByNumberAsync() { $eth = $this->eth; + $eth->provider = $this->asyncHttpProvider; - $eth->submitHashrate( - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - '0xD1FE5700000000000000000000000000D1FE5700000000000000000000000000' - , function ($err, $work) { + // should return reactphp promise + $promise = $eth->getBlockByNumber('latest', false, function ($err, $block) { if ($err !== null) { return $this->assertTrue($err !== null); } - $this->assertTrue(is_bool($work)); + // weird behavior, see https://github.com/web3p/web3.php/issues/16 + $this->assertTrue($block !== null); }); + $this->assertTrue($promise instanceof \React\Promise\PromiseInterface); + \React\Async\await($promise); } /** @@ -803,7 +755,7 @@ public function testUnallowedMethod() $eth->hello(function ($err, $hello) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(true); }); diff --git a/test/unit/EthBatchTest.php b/test/unit/EthBatchTest.php index 976fe20e..591f50e4 100644 --- a/test/unit/EthBatchTest.php +++ b/test/unit/EthBatchTest.php @@ -42,10 +42,36 @@ public function testBatch() $eth->provider->execute(function ($err, $data) { if ($err !== null) { - return $this->fail('Got error!'); + return $this->fail($err->getMessage()); } $this->assertTrue($data[0] instanceof BigNumber); $this->assertTrue($data[1] !== null); }); } + + /** + * testBatchAsync + * + * @return void + */ + public function testBatchAsync() + { + $eth = $this->eth; + $eth->provider = $this->asyncHttpProvider; + + $eth->batch(true); + $eth->protocolVersion(); + $eth->syncing(); + + // should return reactphp promise + $promise = $eth->provider->execute(function ($err, $data) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + $this->assertTrue($data[0] instanceof BigNumber); + $this->assertTrue($data[1] !== null); + }); + $this->assertTrue($promise instanceof \React\Promise\PromiseInterface); + \React\Async\await($promise); + } } \ No newline at end of file diff --git a/test/unit/HttpProviderTest.php b/test/unit/HttpProviderTest.php index f1ef4bfb..57bf0605 100644 --- a/test/unit/HttpProviderTest.php +++ b/test/unit/HttpProviderTest.php @@ -5,6 +5,7 @@ use RuntimeException; use Test\TestCase; use Web3\RequestManagers\HttpRequestManager; +use Web3\RequestManagers\HttpAsyncRequestManager; use Web3\Providers\HttpProvider; use Web3\Methods\Web3\ClientVersion; @@ -57,4 +58,75 @@ public function testBatch() $provider->send($method, null); $provider->execute($callback); } + + /** + * testSendAsync + * + * @return void + */ + public function testSendAsync() + { + $requestManager = new HttpAsyncRequestManager($this->testHost); + $provider = new HttpProvider($requestManager); + $method = new ClientVersion('web3_clientVersion', []); + + // \React\Async\await($provider->send($method, function ($err, $version) { + // if ($err !== null) { + // $this->fail($err->getMessage()); + // } + // $this->assertTrue(is_string($version)); + // })); + $a = $provider->send($method, function ($err, $version) { + if ($err !== null) { + $this->fail($err->getMessage()); + } + $this->assertTrue(is_string($version)); + }); + $b = $provider->send($method, function ($err, $version) { + if ($err !== null) { + $this->fail($err->getMessage()); + } + $this->assertTrue(is_string($version)); + }); + $c = $provider->send($method, function ($err, $version) { + if ($err !== null) { + $this->fail($err->getMessage()); + } + $this->assertTrue(is_string($version)); + }); + \React\Async\await(\React\Async\parallel([ + function () use ($a) { return $a; }, + function () use ($b) { return $b; }, + function () use ($c) { return $c; } + ])); + } + + /** + * testBatchAsync + * + * @return void + */ + public function testBatchAsync() + { + $requestManager = new HttpAsyncRequestManager($this->testHost); + $provider = new HttpProvider($requestManager); + $method = new ClientVersion('web3_clientVersion', []); + $callback = function ($err, $data) { + if ($err !== null) { + $this->fail($err->getMessage()); + } + $this->assertEquals($data[0], $data[1]); + }; + + try { + \React\Async\await($provider->execute($callback)); + } catch (RuntimeException $err) { + $this->assertTrue($err->getMessage() !== true); + } + + $provider->batch(true); + $provider->send($method, null); + $provider->send($method, null); + \React\Async\await($provider->execute($callback)); + } } \ No newline at end of file diff --git a/test/unit/NetApiTest.php b/test/unit/NetApiTest.php index 49142352..0c96d571 100644 --- a/test/unit/NetApiTest.php +++ b/test/unit/NetApiTest.php @@ -79,6 +79,27 @@ public function testListening() }); } + /** + * testPeerCountAsync + * + * @return void + */ + public function testPeerCountAsync() + { + $net = $this->net; + $net->provider = $this->asyncHttpProvider; + + // should return reactphp promise + $promise = $net->peerCount(function ($err, $count) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + $this->assertTrue($count instanceof BigNumber); + }); + $this->assertTrue($promise instanceof \React\Promise\PromiseInterface); + \React\Async\await($promise); + } + /** * testUnallowedMethod * diff --git a/test/unit/NetBatchTest.php b/test/unit/NetBatchTest.php index 8d6d5e31..9fadb1f8 100644 --- a/test/unit/NetBatchTest.php +++ b/test/unit/NetBatchTest.php @@ -43,11 +43,39 @@ public function testBatch() $net->provider->execute(function ($err, $data) { if ($err !== null) { - return $this->fail('Got error!'); + return $this->fail($err->getMessage()); } $this->assertTrue(is_string($data[0])); $this->assertTrue(is_bool($data[1])); $this->assertTrue($data[2] instanceof BigNumber); }); } + + /** + * testBatchAsync + * + * @return void + */ + public function testBatchAsync() + { + $net = $this->net; + $net->provider = $this->asyncHttpProvider; + + $net->batch(true); + $net->version(); + $net->listening(); + $net->peerCount(); + + // should return reactphp promise + $promise = $net->provider->execute(function ($err, $data) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + $this->assertTrue(is_string($data[0])); + $this->assertTrue(is_bool($data[1])); + $this->assertTrue($data[2] instanceof BigNumber); + }); + $this->assertTrue($promise instanceof \React\Promise\PromiseInterface); + \React\Async\await($promise); + } } \ No newline at end of file diff --git a/test/unit/PersonalBatchTest.php b/test/unit/PersonalBatchTest.php index 7af5fff7..0d630abf 100644 --- a/test/unit/PersonalBatchTest.php +++ b/test/unit/PersonalBatchTest.php @@ -41,7 +41,7 @@ public function testBatch() $personal->provider->execute(function ($err, $data) { if ($err !== null) { - return $this->assertTrue($err !== null); + return $this->fail($err->getMessage()); } $this->assertTrue(is_array($data[0])); $this->assertTrue(is_string($data[1])); diff --git a/test/unit/ProviderTest.php b/test/unit/ProviderTest.php index 53ee6a0a..32af5176 100644 --- a/test/unit/ProviderTest.php +++ b/test/unit/ProviderTest.php @@ -20,9 +20,10 @@ public function testSetRequestManager() $this->assertEquals($provider->requestManager->host, 'http://localhost:8545'); - $requestManager = new RequestManager($this->testRinkebyHost); + $requestManager = new RequestManager($this->testHost2); $provider->requestManager = $requestManager; + // there is no setter for request manager $this->assertEquals($provider->requestManager->host, 'http://localhost:8545'); } } \ No newline at end of file diff --git a/test/unit/RequestManagerTest.php b/test/unit/RequestManagerTest.php index 3498ef02..61dabdb2 100644 --- a/test/unit/RequestManagerTest.php +++ b/test/unit/RequestManagerTest.php @@ -18,7 +18,8 @@ public function testSetHost() $this->assertEquals($requestManager->host, 'http://localhost:8545'); $this->assertEquals($requestManager->timeout, 0.1); - $requestManager->host = $this->testRinkebyHost; + // there is no setter for host and timeout + $requestManager->host = $this->testHost2; $requestManager->timeout = 1; $this->assertEquals($requestManager->host, 'http://localhost:8545'); $this->assertEquals($requestManager->timeout, 0.1); diff --git a/test/unit/ShhApiTest.php b/test/unit/ShhApiTest.php index e0fcd0e1..795e8a53 100644 --- a/test/unit/ShhApiTest.php +++ b/test/unit/ShhApiTest.php @@ -465,6 +465,28 @@ public function testVersion() // $this->assertTrue(true); // }); // } + + /** + * testVersionAsync + * + * @return void + */ + public function testVersionAsync() + { + $shh = $this->shh; + $shh->provider = $this->asyncHttpProvider; + + // should return reactphp promise + $promise = $shh->version(function ($err, $version) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + $this->assertTrue(is_string($version)); + }); + $this->assertTrue($promise instanceof \React\Promise\PromiseInterface); + \React\Async\await($promise); + } + /** * testUnallowedMethod * diff --git a/test/unit/ShhBatchTest.php b/test/unit/ShhBatchTest.php index 01093277..b1b3fc7a 100644 --- a/test/unit/ShhBatchTest.php +++ b/test/unit/ShhBatchTest.php @@ -41,34 +41,38 @@ public function testBatch() $shh->provider->execute(function ($err, $data) { if ($err !== null) { - return $this->fail('Got error!'); + return $this->fail($err->getMessage()); } $this->assertTrue(is_string($data[0])); $this->assertTrue(is_string($data[1])); + $this->assertEquals($data[0], $data[1]); }); } /** - * testWrongParam + * testBatchAsync * * @return void */ - // public function testWrongParam() - // { - // $this->expectException(RuntimeException::class); - - // $shh = $this->shh; + public function testBatchAsync() + { + $shh = $this->shh; + $shh->provider = $this->asyncHttpProvider; - // $shh->batch(true); - // $shh->version(); - // $shh->hasIdentity('0'); + $shh->batch(true); + $shh->version(); + $shh->version(); - // $shh->provider->execute(function ($err, $data) { - // if ($err !== null) { - // return $this->fail('Got error!'); - // } - // $this->assertTrue(is_string($data[0])); - // $this->assertFalse($data[1]); - // }); - // } + // should return reactphp promise + $promise = $shh->provider->execute(function ($err, $data) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + $this->assertTrue(is_string($data[0])); + $this->assertTrue(is_string($data[1])); + $this->assertEquals($data[0], $data[1]); + }); + $this->assertTrue($promise instanceof \React\Promise\PromiseInterface); + \React\Async\await($promise); + } } \ No newline at end of file diff --git a/test/unit/Web3BatchTest.php b/test/unit/Web3BatchTest.php index 825a7248..4fbdd534 100644 --- a/test/unit/Web3BatchTest.php +++ b/test/unit/Web3BatchTest.php @@ -48,7 +48,7 @@ public function testBatch() $web3->provider->execute(function ($err, $data) { if ($err !== null) { - return $this->fail('Got error!'); + return $this->fail($err->getMessage()); } $this->assertTrue(is_string($data[0])); $this->assertEquals($data[1], $this->testHash); @@ -72,7 +72,7 @@ public function testWrongParam() $web3->provider->execute(function ($err, $data) { if ($err !== null) { - return $this->fail('Got error!'); + return $this->fail($err->getMessage()); } $this->assertTrue(is_string($data[0])); $this->assertEquals($data[1], $this->testHash);