Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration test #12

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .scrutinizer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ tools:
excluded_dirs: [vendor, build, tests]
external_code_coverage:
timeout: 800
runs: 1
runs: 2

changetracking:
bug_patterns: ["\bfix(?:es|ed)?\b"]
Expand Down
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ matrix:
- php: 7.2
env: PHPUNIT=true COVERAGE=true CODE_STYLE=true
- php: 7.2
env: INTEGRATION=true
env: INTEGRATION=true TREZOR_BRIDGE_VERSION=v2.0.13 COVERAGE=true

install:
- composer install
Expand All @@ -52,5 +52,5 @@ script:
- BRIDGE_PATH=$GOPATH/bin/trezord-go EMULATOR_PATH=tool/emulator/trezor-mcu/build/trezor-emulator64-master tool/run_integration_tests.sh
- tool/run_codestyle_tests.sh

after_success:
after_script:
- if [ "${COVERAGE}" = "true" ] ; then make scrutinizer; fi
36 changes: 36 additions & 0 deletions debug.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);

require "vendor/autoload.php";

use BitWasp\Trezor\Bridge\Client;
use BitWasp\Trezor\Bridge\Http\HttpClient;
use BitWasp\Trezor\Device\Button\DebugButtonAck;
use BitWasp\Trezor\Device\Command\LoadDeviceService;
use BitWasp\Trezor\Device\RequestFactory;

$depth = 0;
$fingerprint = 0;
$numChild = 0;
$chainCode = \Protobuf\Stream::fromString(hex2bin('a86d0945bd342199a130b65255df75199fe09e539d60053003cc1c0e999982a5'));
$privateKey = \Protobuf\Stream::fromString(hex2bin('874c62f2c98f7c94f1a691492825a71e8e9b9251f03c208f37d1ec9c9cda2b24'));
$language = "EN";

$reqFactory = new RequestFactory();
$hdNode = $reqFactory->privateHdNode($depth, $fingerprint, $numChild, $chainCode, $privateKey);
$loadDevice = $reqFactory->loadDeviceWithHdNode($hdNode, $language);

$httpClient = HttpClient::forUri("http://localhost:21325");
$client = new Client($httpClient);
$devices = $client->listDevices()->devices();
if (empty($devices)) {
throw new \RuntimeException("No devices returned");
}

$session = $client->acquire($devices[0]);
$dbgSession = $client->acquire($devices[1]);

$dbgBtnAck = new DebugButtonAck($dbgSession);
$loadService = new LoadDeviceService($dbgBtnAck);
$loaded = $loadService->call($session, $loadDevice);
var_dump($loaded);
19 changes: 15 additions & 4 deletions src/Bridge/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
use BitWasp\Trezor\Bridge\Message\VersionResponse;
use BitWasp\Trezor\Bridge\Schema\ValidatorFactory;
use BitWasp\Trezor\Bridge\Http\HttpClient;
use BitWasp\Trezor\Device\Message;
use BitWasp\Trezor\Device\MessageBase;
use GuzzleHttp\Promise\PromiseInterface;
use Psr\Http\Message\ResponseInterface;

class Client
Expand Down Expand Up @@ -105,7 +106,7 @@ public function listDevices(): ListDevicesResponse
$devices[] = new Device($device);
}

return new ListDevicesResponse($devices);
return new ListDevicesResponse(...$devices);
}

public function listen(Device ...$devices): ListenResponse
Expand All @@ -120,7 +121,7 @@ public function listen(Device ...$devices): ListenResponse
$devices[] = new Device($device);
}

return new ListenResponse($devices);
return new ListenResponse(...$devices);
}

/**
Expand All @@ -147,8 +148,18 @@ public function release(string $sessionId): bool
return true;
}

public function call(string $sessionId, Message $message): Message
public function post(string $sessionId, MessageBase $message)
{
return $this->client->post($sessionId, $message);
}

public function call(string $sessionId, MessageBase $message)
{
return $this->client->call($sessionId, $message);
}

public function callAsync(string $sessionId, MessageBase $message): PromiseInterface
{
return $this->client->callAsync($sessionId, $message);
}
}
47 changes: 37 additions & 10 deletions src/Bridge/Http/HttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
use BitWasp\Trezor\Bridge\Codec\CallMessage;
use BitWasp\Trezor\Bridge\Message\Device;
use BitWasp\Trezor\Device\Message;
use BitWasp\Trezor\Device\MessageBase;
use BitWasp\TrezorProto\MessageType;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Psr7\Response;
use Psr\Http\Message\ResponseInterface;

class HttpClient
Expand Down Expand Up @@ -100,25 +102,50 @@ public function release(string $sessionId): ResponseInterface
]);
}

public function call(string $sessionId, Message $message): Message
public function post(string $sessionId, MessageBase $message)
{
$responsePromise = $this->postAsync($sessionId, $message);
return $responsePromise->wait(true);
}

public function call(string $sessionId, MessageBase $message): Message
{
$responsePromise = $this->callAsync($sessionId, $message);
return $responsePromise->wait(true);
}

public function postAsync(string $sessionId, MessageBase $message)
{
static $prefixLen;
if (null === $prefixLen) {
$prefixLen = strlen("MessageType_");
}

$response = $this->client->post("/call/{$sessionId}", [
return $this->client->postAsync("/post/{$sessionId}", [
'body' => $this->callCodec->encode($message->getType(), $message->getProto()),
]);
}

list ($type, $result) = $this->callCodec->parsePayload($response->getBody());

$messageType = MessageType::valueOf($type);
$protoType = substr($messageType->name(), $prefixLen);
$reader = ["\\BitWasp\\TrezorProto\\{$protoType}", 'fromStream'];
assert(class_exists($reader[0]));
public function callAsync(string $sessionId, MessageBase $message)
{
static $prefixLen;
if (null === $prefixLen) {
$prefixLen = strlen("MessageType_");
}

$protobuf = call_user_func($reader, $result);
return new Message($messageType, $protobuf);
return $this->client->postAsync("/call/{$sessionId}", [
'body' => $this->callCodec->encode($message->getType(), $message->getProto()),
])
->then(function (Response $response) use ($prefixLen): Message {
list ($type, $result) = $this->callCodec->parsePayload($response->getBody());

$messageType = MessageType::valueOf($type);
$protoType = substr($messageType->name(), $prefixLen);
$reader = ["\\BitWasp\\TrezorProto\\{$protoType}", 'fromStream'];
assert(class_exists($reader[0]));

$protobuf = call_user_func($reader, $result);
return new Message($messageType, $protobuf);
});
}
}
8 changes: 1 addition & 7 deletions src/Bridge/Message/DeviceListResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,8 @@ abstract class DeviceListResponse implements \Countable
/**
* @param Device[] $devices
*/
public function __construct(array $devices)
public function __construct(Device... $devices)
{
foreach ($devices as $device) {
if (!($device instanceof Device)) {
throw new \InvalidArgumentException();
}
}

$this->devices = $devices;
}

Expand Down
45 changes: 35 additions & 10 deletions src/Bridge/Session.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
use BitWasp\Trezor\Bridge\Message\Device;
use BitWasp\Trezor\Device\Exception\FailureException;
use BitWasp\Trezor\Device\Message;
use BitWasp\Trezor\Device\MessageBase;
use BitWasp\TrezorProto\Failure;
use Protobuf\Message as ProtoMessage;
use GuzzleHttp\Promise\PromiseInterface;

class Session
{
Expand Down Expand Up @@ -93,20 +94,44 @@ public function getDevice(): Device
}

/**
* @param Message $message
* @return ProtoMessage
* @param MessageBase $request
* @return PromiseInterface
*/
public function sendMessageAsync(MessageBase $request): PromiseInterface
{
$this->assertSessionIsActive();
return $this->client->callAsync($this->getSessionId(), $request)
->then(function (Message $message): \Protobuf\Message {
$proto = $message->getProto();
if ($proto instanceof Failure) {
FailureException::handleFailure($proto);
}

return $proto;
});
}

/**
* @param MessageBase $message
* @return \Protobuf\Message
* @throws FailureException
* @throws InactiveSessionException
*/
public function sendMessage(Message $message): ProtoMessage
public function sendMessage(MessageBase $message): \Protobuf\Message
{
$this->assertSessionIsActive();
$message = $this->client->call($this->getSessionId(), $message);
$proto = $message->getProto();
if ($proto instanceof Failure) {
FailureException::handleFailure($proto);
}
return $this->sendMessageAsync($message)
->wait(true);
}

return $proto;
/**
* @param MessageBase $message
* @throws FailureException
* @throws InactiveSessionException
*/
public function postMessage(MessageBase $message)
{
$this->assertSessionIsActive();
return $this->client->post($this->getSessionId(), $message);
}
}
14 changes: 14 additions & 0 deletions src/Device/Button/ButtonAck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace BitWasp\Trezor\Device\Button;

use BitWasp\Trezor\Bridge\Session;
use BitWasp\TrezorProto\ButtonRequest;
use BitWasp\TrezorProto\ButtonRequestType;

abstract class ButtonAck
{
abstract public function acknowledge(Session $session, ButtonRequest $request, ButtonRequestType $allowType);
}
53 changes: 53 additions & 0 deletions src/Device/Button/DebugButtonAck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace BitWasp\Trezor\Device\Button;

use BitWasp\Trezor\Bridge\Session;
use BitWasp\Trezor\Device\DebugMessage;
use BitWasp\Trezor\Device\Message;
use BitWasp\TrezorProto\ButtonRequest;
use BitWasp\TrezorProto\ButtonRequestType;
use BitWasp\TrezorProto\DebugLinkDecision;

class DebugButtonAck extends ButtonAck
{
/**
* @var Session
*/
private $debug;

/**
* @var bool
*/
private $button;

public function __construct(
Session $debugSession,
bool $button
) {
$this->debug = $debugSession;
$this->button = $button;
}

public function acknowledge(
Session $session,
ButtonRequest $request,
ButtonRequestType $expectedType
): \Protobuf\Message {
$theirType = $request->getCode();
if ($theirType->value() !== $expectedType->value()) {
throw new \RuntimeException("Unexpected button request (expected: {$expectedType->name()}, got {$theirType->name()})");
}

$ack = new \BitWasp\TrezorProto\ButtonAck();

$decision = new DebugLinkDecision();
$decision->setYesNo($this->button);

$success = $session->sendMessageAsync(Message::buttonAck($ack));
$this->debug->postMessage(DebugMessage::decision($decision));
return $success->wait(true);
}
}
23 changes: 23 additions & 0 deletions src/Device/Button/HumanButtonAck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace BitWasp\Trezor\Device\Button;

use BitWasp\Trezor\Bridge\Session;
use BitWasp\Trezor\Device\Message;
use BitWasp\TrezorProto\ButtonRequest;
use BitWasp\TrezorProto\ButtonRequestType;

class HumanButtonAck extends ButtonAck
{
public function acknowledge(Session $session, ButtonRequest $request, ButtonRequestType $expectedType)
{
$theirType = $request->getCode();
if ($theirType->value() !== $expectedType->value()) {
throw new \RuntimeException("Unexpected button request (expected: {$expectedType->name()}, got {$theirType->name()})");
}

return $session->sendMessage(Message::buttonAck(new \BitWasp\TrezorProto\ButtonAck()));
}
}
12 changes: 3 additions & 9 deletions src/Device/Command/DeviceService.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,6 @@

abstract class DeviceService
{
protected function checkPinRequestType(PinMatrixRequest $pinRequest, PinMatrixRequestType $requestType)
{
if ($pinRequest->getType()->value() !== $requestType->value()) {
throw new \RuntimeException("Unexpected pin matrix type (was {$pinRequest->getType()->name()}, not expected type {$requestType->name()})");
}
}

protected function confirmWithButton(ButtonRequest $request, ButtonRequestType $buttonType): Message
{
$theirType = $request->getCode();
Expand All @@ -36,15 +29,16 @@ protected function confirmWithButton(ButtonRequest $request, ButtonRequestType $

protected function provideCurrentPin(PinMatrixRequest $proto, CurrentPinInputInterface $currentPinInput): Message
{
$this->checkPinRequestType($proto, PinMatrixRequestType::PinMatrixRequestType_Current());
if ($proto->getType()->value() !== PinMatrixRequestType::PinMatrixRequestType_Current()->value()) {
throw new \RuntimeException("Unexpected pin matrix type (was {$proto->getType()->name()}, not expected type ".PinMatrixRequestType::PinMatrixRequestType_Current()->name().")");
}

$pinMatrixAck = new PinMatrixAck();
$pinMatrixAck->setPin($currentPinInput->getPin());

return Message::pinMatrixAck($pinMatrixAck);
}


protected function provideCurrentPassphrase(CurrentPassphraseInputInterface $passphraseInput): Message
{
$passphraseAck = new PassphraseAck();
Expand Down
Loading