fix(wallet): canonicalize root sequencer endpoints

This commit is contained in:
Ricardo Guilherme Schmidt
2026-07-18 19:56:38 -03:00
parent 3bb02e1531
commit d01e241521
2 changed files with 86 additions and 4 deletions
+13 -4
View File
@@ -56,6 +56,15 @@ QByteArray rpcBody(const QString& method, const QJsonArray& params)
}).toJson(QJsonDocument::Compact);
}
QUrl canonicalEndpoint(QUrl endpoint)
{
// Both forms issue requests to the HTTP root, so they must share cache
// and credential identity.
if (endpoint.path() == QLatin1String("/"))
endpoint.setPath({});
return endpoint;
}
WalletAccountRead accountRead(const QJsonObject& value, const QString& fallbackId)
{
WalletAccountRead read;
@@ -90,8 +99,8 @@ bool SequencerClient::configure(const QString& configPath,
if (configValid) {
const QJsonDocument document = QJsonDocument::fromJson(file.readAll());
const QJsonObject config = document.object();
configuredEndpoint = QUrl(
config.value(QStringLiteral("sequencer_addr")).toString());
configuredEndpoint = canonicalEndpoint(QUrl(
config.value(QStringLiteral("sequencer_addr")).toString()));
const QString scheme = configuredEndpoint.scheme().toLower();
configValid = document.isObject()
&& configuredEndpoint.isValid()
@@ -108,8 +117,8 @@ bool SequencerClient::configure(const QString& configPath,
}
}
QUrl endpoint = effectiveEndpoint.isEmpty()
? configuredEndpoint : QUrl(effectiveEndpoint);
QUrl endpoint = canonicalEndpoint(effectiveEndpoint.isEmpty()
? configuredEndpoint : QUrl(effectiveEndpoint));
const QString scheme = endpoint.scheme().toLower();
const bool valid = endpoint.isValid()
&& !endpoint.host().isEmpty()
@@ -748,6 +748,79 @@ int main(int argc, char** argv)
"matching endpoint should retain configured credentials"))
return 1;
LocalRpcServer rootEndpointServer;
if (!expect(rootEndpointServer.listen(),
"root endpoint sequencer should listen"))
return 1;
QTemporaryFile rootEndpointConfig;
if (!expect(rootEndpointConfig.open(),
"root endpoint config should open"))
return 1;
rootEndpointConfig.write(QJsonDocument(QJsonObject {
{ QStringLiteral("sequencer_addr"), rootEndpointServer.endpoint() + QLatin1Char('/') },
{ QStringLiteral("basic_auth"), QJsonObject {
{ QStringLiteral("username"), QStringLiteral("user") },
{ QStringLiteral("password"), QStringLiteral("pass") },
} },
}).toJson(QJsonDocument::Compact));
rootEndpointConfig.flush();
FakeAmmClient rootEndpointClient;
SequencerClient rootEndpointSequencer(&rootEndpointClient);
if (!expect(rootEndpointSequencer.configure(rootEndpointConfig.fileName()),
"root endpoint config should configure"))
return 1;
const QString rootEndpointAccount(64, QLatin1Char('4'));
QVector<WalletAccountRead> rootEndpointReads;
if (!expect(waitForAccounts(rootEndpointSequencer, { rootEndpointAccount }, false,
&rootEndpointReads),
"root endpoint initial read should complete"))
return 1;
if (!expect(rootEndpointServer.requestCount() == 1
&& rootEndpointServer.lastRequest().toLower().contains(
"authorization: basic dxnlcjpwyxnz"),
"configured root endpoint should use configured credentials"))
return 1;
if (!expect(rootEndpointSequencer.configure(
rootEndpointConfig.fileName(), rootEndpointServer.endpoint()),
"slash-equivalent endpoint should configure"))
return 1;
rootEndpointReads.clear();
if (!expect(waitForAccounts(rootEndpointSequencer, { rootEndpointAccount }, false,
&rootEndpointReads),
"slash-equivalent cached read should complete"))
return 1;
if (!expect(rootEndpointServer.requestCount() == 1,
"slash-equivalent endpoint should preserve account cache"))
return 1;
const QString rootEndpointFreshAccount(64, QLatin1Char('5'));
if (!expect(waitForAccounts(rootEndpointSequencer, { rootEndpointFreshAccount }, true,
&rootEndpointReads),
"slash-equivalent forced read should complete"))
return 1;
if (!expect(rootEndpointServer.requestCount() == 2
&& rootEndpointServer.lastRequest().toLower().contains(
"authorization: basic dxnlcjpwyxnz"),
"slash-equivalent endpoint should retain configured credentials"))
return 1;
if (!expect(rootEndpointSequencer.configure(
rootEndpointConfig.fileName(),
rootEndpointServer.endpoint() + QStringLiteral("/rpc")),
"path-changing endpoint should configure"))
return 1;
rootEndpointReads.clear();
if (!expect(waitForAccounts(rootEndpointSequencer, { rootEndpointAccount }, false,
&rootEndpointReads),
"path-changing endpoint read should complete"))
return 1;
if (!expect(rootEndpointServer.requestCount() == 3
&& !rootEndpointServer.lastRequest().toLower().contains(
"authorization:"),
"path-changing endpoint should clear cache and credentials"))
return 1;
LocalRpcServer staleContextServer;
if (!expect(staleContextServer.listen(),
"stale-context sequencer should listen"))