added testing tool
This commit is contained in:
parent
6cdb3da101
commit
e0caa14fc5
45
README.md
45
README.md
|
|
@ -106,6 +106,51 @@ const result = await response.json();
|
|||
The wallet can generate the handshake proof when it is unlocked and reuse it
|
||||
for later API calls. It does not send its private key or wallet decryption key.
|
||||
|
||||
### Test Tool
|
||||
|
||||
The CLI-only test tool sends an existing wallet handshake proof through the
|
||||
same headers a browser wallet will use. Set the credentials in the current
|
||||
shell:
|
||||
|
||||
```bash
|
||||
export CONTRACTLESS_TEST_ADDRESS="$(jq -r '.short_address' /path/to/wallet)"
|
||||
export CONTRACTLESS_TEST_PUBLIC_KEY="$(jq -r '.public_key' /path/to/wallet)"
|
||||
```
|
||||
|
||||
Generate the handshake signature with the Contractless `sign_message` tool:
|
||||
|
||||
```bash
|
||||
./sign_message "aced"
|
||||
```
|
||||
|
||||
Enter the same wallet path and decryption key. Copy only the hexadecimal value
|
||||
printed after `signature:`:
|
||||
|
||||
```bash
|
||||
export CONTRACTLESS_TEST_SIGNATURE='1332-character-signature-from-sign-message'
|
||||
```
|
||||
|
||||
`sign_message` and the node handshake both sign the Skein-256 hash of the exact
|
||||
text `aced`, so this produces the proof expected by the API.
|
||||
|
||||
Then test the default `/api/v1/height` route:
|
||||
|
||||
```bash
|
||||
php tools/test_api.php https://api.contractless.dev
|
||||
```
|
||||
|
||||
An alternative GET route may be provided:
|
||||
|
||||
```bash
|
||||
php tools/test_api.php \
|
||||
https://api.contractless.dev \
|
||||
/api/v1/network
|
||||
```
|
||||
|
||||
The tool never accepts or loads a private key. It tests the exact public proof
|
||||
that applications will submit to the API and returns a nonzero exit status
|
||||
when the HTTP request fails.
|
||||
|
||||
## Initial Endpoints
|
||||
|
||||
All responses use JSON. Successful responses contain `success: true` and a
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
http_response_code(404);
|
||||
exit;
|
||||
}
|
||||
|
||||
$baseUrl = rtrim((string) ($argv[1] ?? ''), '/');
|
||||
$address = strtolower(trim(
|
||||
(string) (getenv('CONTRACTLESS_TEST_ADDRESS') ?: ''),
|
||||
));
|
||||
$publicKey = strtolower(trim(
|
||||
(string) (getenv('CONTRACTLESS_TEST_PUBLIC_KEY') ?: ''),
|
||||
));
|
||||
$signature = strtolower(trim(
|
||||
(string) (getenv('CONTRACTLESS_TEST_SIGNATURE') ?: ''),
|
||||
));
|
||||
$route = (string) ($argv[2] ?? '/api/v1/height');
|
||||
|
||||
if (
|
||||
$baseUrl === ''
|
||||
|| filter_var($baseUrl, FILTER_VALIDATE_URL) === false
|
||||
|| !str_starts_with($baseUrl, 'https://')
|
||||
) {
|
||||
fwrite(
|
||||
STDERR,
|
||||
"Usage: php tools/test_api.php https://api.example.com [/api/v1/route]\n",
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
if (!str_starts_with($route, '/api/v1')) {
|
||||
fwrite(STDERR, "The test route must begin with /api/v1.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (preg_match('/^[a-f0-9]{40}\.(clc|cltc)$/', $address) !== 1) {
|
||||
fwrite(STDERR, "Set CONTRACTLESS_TEST_ADDRESS to a canonical wallet address.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (
|
||||
preg_match('/^(?:[a-f0-9]{1794}|[a-f0-9]{1796})$/', $publicKey) !== 1
|
||||
) {
|
||||
fwrite(
|
||||
STDERR,
|
||||
"Set CONTRACTLESS_TEST_PUBLIC_KEY to the wallet's Falcon public-key hex.\n",
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
if (preg_match('/^[a-f0-9]{1332}$/', $signature) !== 1) {
|
||||
fwrite(
|
||||
STDERR,
|
||||
"Set CONTRACTLESS_TEST_SIGNATURE to the Falcon handshake signature hex.\n",
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
$context = stream_context_create([
|
||||
'http' => [
|
||||
'method' => 'GET',
|
||||
'header' => implode("\r\n", [
|
||||
'Accept: application/json',
|
||||
'X-Contractless-Address: ' . $address,
|
||||
'X-Contractless-Public-Key: ' . $publicKey,
|
||||
'X-Contractless-Signature: ' . $signature,
|
||||
]),
|
||||
'ignore_errors' => true,
|
||||
'timeout' => 20,
|
||||
],
|
||||
]);
|
||||
$body = file_get_contents($baseUrl . $route, false, $context);
|
||||
if ($body === false) {
|
||||
throw new RuntimeException('The API request could not be completed.');
|
||||
}
|
||||
|
||||
$responseHeaders = $http_response_header ?? [];
|
||||
$statusLine = (string) ($responseHeaders[0] ?? '');
|
||||
if (preg_match('/\s(\d{3})\s/', $statusLine, $match) !== 1) {
|
||||
throw new RuntimeException('The API returned an invalid HTTP response.');
|
||||
}
|
||||
|
||||
$status = (int) $match[1];
|
||||
echo "HTTP $status\n";
|
||||
try {
|
||||
$decoded = json_decode($body, true, flags: JSON_THROW_ON_ERROR);
|
||||
echo json_encode(
|
||||
$decoded,
|
||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR,
|
||||
) . PHP_EOL;
|
||||
} catch (JsonException) {
|
||||
echo $body . PHP_EOL;
|
||||
}
|
||||
|
||||
exit($status >= 200 && $status < 300 ? 0 : 1);
|
||||
} catch (Throwable $error) {
|
||||
fwrite(STDERR, 'API test failed: ' . $error->getMessage() . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
Loading…
Reference in New Issue