fix(amm): align reads with adopted wallet endpoint

This commit is contained in:
Ricardo Guilherme Schmidt 2026-07-17 20:32:42 -03:00
parent cfaaba3bce
commit 8023195ec8
No known key found for this signature in database
GPG Key ID: 1396EA17DE132FFE
4 changed files with 89 additions and 12 deletions

View File

@ -210,7 +210,7 @@ void AmmUiBackend::syncWalletState()
setSequencerAddr(state.sequencerAddress);
setSequencerReachable(state.sequencerReachable);
m_sequencer->configure(state.configPath);
m_sequencer->configure(state.configPath, state.sequencerAddress);
const bool addressChanged = previousAddress != state.sequencerAddress;
if ((walletCouldSubmit && !state.canSubmit()) || addressChanged)

View File

@ -80,20 +80,24 @@ SequencerClient::SequencerClient(AmmClient* client, QObject* parent)
{
}
bool SequencerClient::configure(const QString& configPath)
bool SequencerClient::configure(const QString& configPath,
const QString& effectiveEndpoint)
{
QUrl endpoint;
QUrl configuredEndpoint;
QByteArray authorization;
QFile file(configPath);
bool valid = file.open(QIODevice::ReadOnly);
if (valid) {
bool configValid = file.open(QIODevice::ReadOnly);
if (configValid) {
const QJsonDocument document = QJsonDocument::fromJson(file.readAll());
const QJsonObject config = document.object();
endpoint = QUrl(config.value(QStringLiteral("sequencer_addr")).toString());
const QString scheme = endpoint.scheme().toLower();
valid = document.isObject() && endpoint.isValid() && !endpoint.host().isEmpty()
configuredEndpoint = QUrl(
config.value(QStringLiteral("sequencer_addr")).toString());
const QString scheme = configuredEndpoint.scheme().toLower();
configValid = document.isObject()
&& configuredEndpoint.isValid()
&& !configuredEndpoint.host().isEmpty()
&& (scheme == QStringLiteral("http") || scheme == QStringLiteral("https"));
if (valid) {
if (configValid) {
const QJsonObject basicAuth = config.value(QStringLiteral("basic_auth")).toObject();
const QString username = basicAuth.value(QStringLiteral("username")).toString();
const QString password = basicAuth.value(QStringLiteral("password")).toString();
@ -103,10 +107,18 @@ bool SequencerClient::configure(const QString& configPath)
}
}
}
if (!valid) {
QUrl endpoint = effectiveEndpoint.isEmpty()
? configuredEndpoint : QUrl(effectiveEndpoint);
const QString scheme = endpoint.scheme().toLower();
const bool valid = endpoint.isValid()
&& !endpoint.host().isEmpty()
&& (scheme == QStringLiteral("http") || scheme == QStringLiteral("https"))
&& (configValid || !effectiveEndpoint.isEmpty());
if (!valid)
endpoint = QUrl();
if (!configValid || configuredEndpoint != endpoint)
authorization.clear();
}
if (endpoint == m_endpoint && authorization == m_authorization)
return valid;

View File

@ -25,7 +25,8 @@ public:
explicit SequencerClient(AmmClient* client, QObject* parent = nullptr);
bool configure(const QString& configPath);
bool configure(const QString& configPath,
const QString& effectiveEndpoint = {});
QString endpoint() const { return m_endpoint.toString(); }
bool isConfigured() const { return m_endpoint.isValid() && !m_endpoint.isEmpty(); }
void applyAuthorization(QNetworkRequest& request) const;

View File

@ -53,6 +53,10 @@ namespace {
}
int requestCount() const { return m_requestCount; }
QByteArray lastRequest() const
{
return m_lastRequest;
}
void failNextRequest() { ++m_failuresRemaining; }
void holdResponses() { m_holdResponses = true; }
void releaseNextResponse()
@ -108,6 +112,7 @@ namespace {
return;
++m_requestCount;
m_lastRequest = request;
const bool fail = m_failuresRemaining > 0;
if (fail)
--m_failuresRemaining;
@ -121,6 +126,7 @@ namespace {
QTcpServer m_server;
QHash<QTcpSocket*, QByteArray> m_requests;
QByteArray m_lastRequest;
int m_requestCount = 0;
int m_failuresRemaining = 0;
bool m_holdResponses = false;
@ -480,6 +486,64 @@ int main(int argc, char** argv)
"stale quote should have no wallet side effects"))
return 1;
LocalRpcServer configuredEndpointServer;
LocalRpcServer adoptedEndpointServer;
if (!expect(configuredEndpointServer.listen()
&& adoptedEndpointServer.listen(),
"endpoint-alignment sequencers should listen"))
return 1;
QTemporaryFile endpointConfig;
if (!expect(endpointConfig.open(), "endpoint-alignment config should open"))
return 1;
endpointConfig.write(QJsonDocument(QJsonObject {
{ QStringLiteral("sequencer_addr"), configuredEndpointServer.endpoint() },
{ QStringLiteral("basic_auth"), QJsonObject {
{ QStringLiteral("username"), QStringLiteral("user") },
{ QStringLiteral("password"), QStringLiteral("pass") },
} },
}).toJson(QJsonDocument::Compact));
endpointConfig.flush();
FakeAmmClient endpointClient;
SequencerClient alignedSequencer(&endpointClient);
if (!expect(alignedSequencer.configure(
endpointConfig.fileName(), adoptedEndpointServer.endpoint()),
"adopted wallet endpoint should configure"))
return 1;
if (!expect(alignedSequencer.endpoint() == adoptedEndpointServer.endpoint(),
"adopted wallet endpoint should override stale config"))
return 1;
QVector<WalletAccountRead> adoptedEndpointReads;
const QString endpointAccount(64, QLatin1Char('3'));
if (!expect(waitForAccounts(
alignedSequencer, { endpointAccount }, false,
&adoptedEndpointReads),
"adopted endpoint read should complete"))
return 1;
if (!expect(configuredEndpointServer.requestCount() == 0
&& adoptedEndpointServer.requestCount() == 1,
"direct reads should follow adopted wallet endpoint"))
return 1;
if (!expect(!adoptedEndpointServer.lastRequest().toLower().contains(
"authorization:"),
"stale config credentials must not reach adopted endpoint"))
return 1;
if (!expect(alignedSequencer.configure(
endpointConfig.fileName(), configuredEndpointServer.endpoint()),
"matching wallet endpoint should configure"))
return 1;
QVector<WalletAccountRead> configuredEndpointReads;
if (!expect(waitForAccounts(
alignedSequencer, { endpointAccount }, false,
&configuredEndpointReads),
"matching endpoint read should complete"))
return 1;
if (!expect(configuredEndpointServer.requestCount() == 1
&& configuredEndpointServer.lastRequest().toLower().contains(
"authorization: basic dxnlcjpwyxnz"),
"matching endpoint should retain configured credentials"))
return 1;
LocalRpcServer server;
if (!expect(server.listen(), "local sequencer should listen"))
return 1;