Problem
WalletRegistry._build_public_key (app/services/wallet_registry.py):
@staticmethod
def _build_public_key() -> str:
return f"G{uuid4().hex.upper()}"
uuid4().hex is 32 chars, so the result is 33 characters total. Stellar public keys are exactly 56 chars, and wallet_address.normalize (app/utils/wallet_address.py) rejects anything not exactly 56 (Stellar public keys must be exactly 56 characters (got 33)).
Consequences:
- Every wallet created without a user-supplied key gets an invalid key:
create_wallet stores a 33-char string in WalletORM.public_key (a String(56) unique column), so the platform's own data violates its validator; any code path that later validates the stored key (GDPR export, linking, funding checks) fails or misbehaves.
- Keys are not unique in any real sense:
uuid4().hex is random but the string is not a Stellar key at all; get_balance/get_status treat funded/trustline_ready flags as authoritative, so the fake key never reaches the network — but the moment a real Horizon fetch is added (tracked separately), every existing row is invalid.
- The checksum validator (tracked separately) will make this worse: generation and validation currently disagree, and no test asserts the generated key satisfies
wallet_address.is_valid.
Root cause
The generator was written as a placeholder ("G" + hex) and never reconciled with the address grammar the rest of the codebase enforces.
Why this is architecturally hard
- Generating a real Stellar key pair requires an ed25519 keypair (e.g. the
stellar-sdk key generation) and deriving the G... address — a dependency/design decision the repo has not made; alternatively, wallet creation should require a client-supplied key validated by wallet_address.normalize, removing the server-side generator.
- Existing rows with 33-char keys must be handled: a migration to invalidate/repair them or a documented deprecation of the generated-key path.
- The create-wallet response message ("Please fund with at least 1 XLM") implies a real network account; if keys are simulated, the response should say so explicitly (same class of issue as the simulated balances).
Proposed design
Either generate real keypairs (via stellar-sdk or a documented KMS flow) or require validated client-supplied keys in WalletCreateRequest; add a test asserting every stored public_key passes wallet_address.is_valid, and a data check for existing rows.
Acceptance criteria
Service
Tests
Out of scope
CRC16 checksum validation (tracked separately) and simulated balances (tracked separately).
Getting started
pytest tests/test_wallet_persistence.py -q
make typecheck
Good first files to read: app/services/wallet_registry.py, app/utils/wallet_address.py, app/models/orm/wallet.py.
Problem
WalletRegistry._build_public_key(app/services/wallet_registry.py):uuid4().hexis 32 chars, so the result is 33 characters total. Stellar public keys are exactly 56 chars, andwallet_address.normalize(app/utils/wallet_address.py) rejects anything not exactly 56 (Stellar public keys must be exactly 56 characters (got 33)).Consequences:
create_walletstores a 33-char string inWalletORM.public_key(aString(56)unique column), so the platform's own data violates its validator; any code path that later validates the stored key (GDPR export, linking, funding checks) fails or misbehaves.uuid4().hexis random but the string is not a Stellar key at all;get_balance/get_statustreatfunded/trustline_readyflags as authoritative, so the fake key never reaches the network — but the moment a real Horizon fetch is added (tracked separately), every existing row is invalid.wallet_address.is_valid.Root cause
The generator was written as a placeholder ("G" + hex) and never reconciled with the address grammar the rest of the codebase enforces.
Why this is architecturally hard
stellar-sdkkey generation) and deriving theG...address — a dependency/design decision the repo has not made; alternatively, wallet creation should require a client-supplied key validated bywallet_address.normalize, removing the server-side generator.Proposed design
Either generate real keypairs (via
stellar-sdkor a documented KMS flow) or require validated client-supplied keys inWalletCreateRequest; add a test asserting every storedpublic_keypasseswallet_address.is_valid, and a data check for existing rows.Acceptance criteria
Service
public_keystored passeswallet_address.is_valid.Tests
Out of scope
CRC16 checksum validation (tracked separately) and simulated balances (tracked separately).
Getting started
Good first files to read:
app/services/wallet_registry.py,app/utils/wallet_address.py,app/models/orm/wallet.py.