Repository navigation
fix(server): read the signers of real testnet accounts in wallet sign-in - #158
Conversation
Wallet sign-in failed for every real testnet account with
AuthenticationUnavailable ("The account signers could not be read").
The general-purpose LedgerEntryData decoder of stellar_dart throws on a
real account entry, which carries ledger extensions, and the failure was
mapped to ChainUnavailable. The tests only used a minimal synthetic entry.
Read the fixed AccountEntry layout up to the signers by hand and ignore
the extension tail. A truncated or foreign entry is still unreadable and
never falls back to the master key.
Add regression tests with the recorded real entry of a funded testnet
account, and with truncated and non-account entries.
Deploying puls3 with
|
| Latest commit: |
c9f7acd
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://71b596c7.puls3-4lw.pages.dev |
| Branch Preview URL: | https://fix-136-sep10-real-account-s.puls3-4lw.pages.dev |
XxHugheadxX
left a comment
There was a problem hiding this comment.
Approved. Without this fix, wallet sign-in from the app (#149) can never work against a real testnet account. The diagnosis matches the code: the general LedgerEntryData decoder throws on the extension tail, and that error ended as AuthenticationUnavailable.
What I checked on 0f75a3d:
-
Layout against the XDR spec. The fields come in this order: entry type,
PublicKeyaccount id, balance, sequence number,numSubEntries, optionalinflationDest, flags,string32home domain (padded),thresholds[4], thenSigner<20>(key union + weight). The decoder stops beforeexton purpose. All fourSignerKeyarms are handled, including the variablepayload<64>ofed25519SignedPayload. -
Failure handling. A truncated, foreign or oversized entry throws inside
_authorityOf, andauthorityOfturns that intoChainUnavailable. It never falls back to the master key. -
Locally:
dart analyze --fatal-infos: no issues.dart test test/unit: 561 passed, including the new real-entry and truncated-entry tests and the SEP-10 verifier tests.- CI is green.
-
Paths the PR's tests do not reach. I ran a throwaway probe, not committed, that read back hand-built entries:
- an
inflationDestthat is set; - home domains of length 1, 2, 3, 4 and 9, which covers every padding case;
- a
hashXsigner; - an
ed25519SignedPayloadsigner with payloads of 0, 1, 3, 4, 5 and 64 bytes.
All 14 cases decode to the right thresholds, and to only the ed25519 signer with its weight.
- an
Non-blocking, for a follow-up:
stellar_dart2.3.0 serializesSignerKeyEd25519SignedPayloadwithout its payload.Signer.toXDR()gives 40 bytes: type, key and weight, with no payload length. So a fixture built with it would encode a wrong entry. I had to write the signed-payload bytes by hand from the spec. This is one more reason the hand decoder is the right call.- One test with an
inflationDest, a non-empty home domain and a signed-payload signer would pin those three branches. Today only the probe above covers them.
Refs #136
Summary
Since #147, a wallet signs in with a SEP-10 challenge and the server checks the signature against the account's signers on the ledger. That check could never pass for a real testnet account:
RpcChainAccounts.authorityOfdecoded the account entry with the generalLedgerEntryDatadecoder ofstellar_dart, which throws on a real entry (it carries ledger extensions). The server mapped the failure toChainUnavailable, soverifyChallengeansweredAuthenticationUnavailable: The account signers could not be read., and the app showed "Sign-in with your wallet is not available yet". The tests passed because they only used a minimal synthetic entry.This PR reads the fixed
AccountEntrylayout by hand, up to the signers, and ignores the extension tail. Thresholds and ed25519 signers are read exactly as before. Anything that does not fit the layout is still unreadable (ChainUnavailable), never a master-key account.It is server-only and does not touch the app PRs (#149, #150). Without it, wallet sign-in from the app fails for any real account, so #149 needs it to work end to end.
Acceptance criteria
Of #136 (this PR does not complete the issue, hence
Refs):createHireworks for it. This PR fixes the real-account path. Verified by hand (below). The existing integration tests use a fake chain, so they did not cover it.Verification evidence
Regression test first (red), against the recorded real entry of a funded testnet account (
test/fixtures/escrow_relay/get_ledger_entries_account.json):After the fix it decodes to master weight 1, medium threshold 0, no extra signers.
Manual, local server against Stellar testnet, funded account
GABKNX5HWXUYTWF6ORIKYO2NHTAPJ67OIF46TPP2IEMVGWXGBQXIHF5H, challenge signed with its key:Server log after the fix, from the browser flow:
walletAuth ok code=okforcreateChallengeandverifyChallenge, thenhire.createHireandhire.submitEscrowCallwith the session user.Notes for reviewers
serverpod generateis not affected. No new environment variable or secret.