feat(suite-sync): handle quota manager disabled

This commit is contained in:
juriczech
2026-02-13 10:38:05 +01:00
committed by Bohdan Juříček
parent 0ed2ee89a5
commit dfe2f51eee
16 changed files with 63 additions and 51 deletions

View File

@@ -16,7 +16,7 @@ export type CreateEvoluStorageFactoryDeps = CreateEvoluInstanceDep;
*/
export const createEvoluStorageFactory =
(deps: CreateEvoluStorageFactoryDeps): CreateSuiteStorage =>
({ suiteSyncOwner, relayUrl }): SuiteSyncStorage => {
({ suiteSyncOwner }): SuiteSyncStorage => {
/**
* Dispose function of the connected owner. When owner is changed
* (for example for RelayUrl change, this needs to be called).
@@ -43,8 +43,6 @@ export const createEvoluStorageFactory =
ownerDispose = evolu.useOwner(syncOwner);
};
updateRelayUrl(relayUrl); // This updates the relay
return {
data: {
accounts: new EvoluAccountTable(evolu as unknown as Evolu<typeof AccountSchema>),

View File

@@ -1,3 +1,4 @@
import { QuotaManagerDisabledErrType } from '@suite-common/suite-sync-types';
import { isDevEnv } from '@suite-common/suite-utils';
/**
@@ -24,3 +25,7 @@ export const DEFAULT_QUOTA_MANAGER_URL = isDevEnv
* Header used for signing add space to owner requests.
*/
export const EVOLU_SIGN_ADD_SPACE_TO_OWNER_REQUEST_HEADER = 'EvoluAddSpaceToOwnerV1';
export const QuotaManagerDisabled = (): QuotaManagerDisabledErrType => ({
type: 'QuotaManagerDisabled',
});

View File

@@ -49,4 +49,4 @@ export {
/**
* Constants.
*/
export { DEFAULT_DEVICE_SIZE_QUOTA } from './constants';
export { DEFAULT_DEVICE_SIZE_QUOTA, QuotaManagerDisabled } from './constants';

View File

@@ -34,12 +34,4 @@ export const selectHasDeviceAllowance = (
state: WithSuiteSyncQuotaManagerState,
deviceId: string,
walletDescriptor: WalletDescriptor,
) => {
// Return success - ignore allowance if case quota manager is disabled.
if (!selectIsQuotaManagerEnabled(state)) return true;
return (
selectIsDeviceRegistered(state, deviceId) &&
selectHasOwnerAllowance(state, walletDescriptor)
);
};
) => selectIsDeviceRegistered(state, deviceId) && selectHasOwnerAllowance(state, walletDescriptor);

View File

@@ -1,10 +1,4 @@
import { WalletDescriptor } from '@suite-common/wallet-types';
import { Branded } from '@trezor/type-utils';
export type SuiteSyncOwnerIdHashed = string & Branded<'SuiteSyncOwnerIdHashed'>;
export const asSuiteSyncOwnerIdHashed = (value: string): SuiteSyncOwnerIdHashed =>
value as SuiteSyncOwnerIdHashed;
export type RegisteredDevice = {
deviceId: string;

View File

@@ -20,7 +20,6 @@ export type SuiteSyncStorage = {
type SuiteStorageCreatorParams = {
suiteSyncOwner: SuiteSyncOwner;
relayUrl: string;
};
/**

View File

@@ -4,10 +4,8 @@ import { WalletDescriptor } from '@suite-common/wallet-types';
import { StaticSessionId } from '@trezor/connect';
import { Result } from '@trezor/type-utils';
import {
SuiteSyncUnavailableOnDeviceErrorType,
WriteModeRequiredForAllocationErrType,
} from '../refreshSuiteSyncKeys';
import { WriteModeRequiredForAllocationErrType } from '../quotaManagerTypes';
import { SuiteSyncUnavailableOnDeviceErrorType } from '../refreshSuiteSyncKeys';
type SubscribeSuiteSyncDataParams = {
deviceStaticSessionId: StaticSessionId;

View File

@@ -11,8 +11,11 @@ export type {
RefreshSuiteSyncKeys,
RefreshSuiteSyncKeysDep,
RefreshSuiteSyncKeysResult,
WriteModeRequiredForAllocationErrType,
} from './refreshSuiteSyncKeys';
export type {
QuotaManagerDisabledErrType,
WriteModeRequiredForAllocationErrType,
} from './quotaManagerTypes';
export type { TurnOffSuiteSyncDep, TurnOffSuiteSync } from './turnOffSuiteSync';
export type { TurnOnSuiteSyncDep, TurnOnSuiteSync } from './turnOnSuiteSync';
export type { SuiteSyncUnavailableOnDeviceErrorType } from './refreshSuiteSyncKeys';

View File

@@ -3,7 +3,7 @@ import { DelegatedIdentityKey } from '@suite-common/suite-types';
import { WalletDescriptor } from '@suite-common/wallet-types';
import { Result } from '@trezor/type-utils';
import { WriteModeRequiredForAllocationErrType } from '../refreshSuiteSyncKeys';
import { WriteModeRequiredForAllocationErrType } from '../quotaManagerTypes';
export type HttpErrType = { type: 'HttpError' };

View File

@@ -0,0 +1,7 @@
export type WriteModeRequiredForAllocationErrType = {
type: 'WriteModeRequiredForAllocation';
};
export type QuotaManagerDisabledErrType = {
type: 'QuotaManagerDisabled';
};

View File

@@ -17,10 +17,6 @@ export type SuiteSyncUnavailableOnDeviceErrorType = {
type: 'SuiteSyncUnavailableOnDeviceError';
};
export type WriteModeRequiredForAllocationErrType = {
type: 'WriteModeRequiredForAllocation';
};
export type RefreshSuiteSyncKeysResult = {
owner: SuiteSyncOwner;
delegatedKey: DelegatedIdentityKey;

View File

@@ -3,10 +3,8 @@ import { DeviceCancelledErrType, DeviceErrorType } from '@suite-common/suite-typ
import { StaticSessionId } from '@trezor/connect';
import { Result } from '@trezor/type-utils';
import {
SuiteSyncUnavailableOnDeviceErrorType,
WriteModeRequiredForAllocationErrType,
} from '../refreshSuiteSyncKeys';
import { WriteModeRequiredForAllocationErrType } from '../quotaManagerTypes';
import { SuiteSyncUnavailableOnDeviceErrorType } from '../refreshSuiteSyncKeys';
export type SuiteSyncFirmwareUpgradeNeededDeviceErrorType = {
type: 'SuiteSyncFirmwareUpgradeNeededDeviceErrorType';

View File

@@ -4,7 +4,10 @@ import { EnsureDelegatedIdentityKeyDep } from '@suite-common/delegated-identity-
import { toGetter } from '@suite-common/dependency-injection';
import { selectAllDeviceStaticIds, selectDeviceByStaticSessionId } from '@suite-common/device';
import { PlatformEncryptionDep } from '@suite-common/platform-encryption';
import { selectHasDeviceAllowance } from '@suite-common/suite-sync-quota-manager';
import {
selectHasDeviceAllowance,
selectIsQuotaManagerEnabled,
} from '@suite-common/suite-sync-quota-manager';
import { CreateSuiteStorage, CreateSuiteSyncOwnerDep } from '@suite-common/suite-sync-storage';
import {
SuiteSync,
@@ -103,6 +106,7 @@ export const createSuiteSyncCompositionRoot = (
getDeviceForStaticSessionId,
hasAllowance: ({ walletDescriptor, deviceId }) =>
selectHasDeviceAllowance(deps.getState(), deviceId ?? null, walletDescriptor),
getIsQuotaManagerEnabled: toGetter(deps.getState, selectIsQuotaManagerEnabled),
});
const suiteSyncErrorHandler: SuiteSyncErrorHandler = createSuiteSyncErrorHandler({

View File

@@ -1,15 +1,20 @@
import { Dispatch } from '@reduxjs/toolkit';
import {
QuotaManagerDisabled,
WriteModeRequiredForAllocation,
ensureDeviceHasQuotaThunk,
ensureOwnerHasAllocatedQuotaThunk,
} from '@suite-common/suite-sync-quota-manager';
import type { WriteModeRequiredForAllocationErrType } from '@suite-common/suite-sync-types';
import {
QuotaManagerDisabledErrType,
WriteModeRequiredForAllocationErrType,
} from '@suite-common/suite-sync-types';
import { DelegatedIdentityKey, SuiteSyncOwner } from '@suite-common/suite-types';
import { isTrezorDeviceWithState, parseDeviceStaticSessionId } from '@suite-common/wallet-utils';
import { StaticSessionId } from '@trezor/connect';
import { Result, err, ok } from '@trezor/type-utils';
import { isNotNull, isNotNullOrUndefined } from '@trezor/utils';
import { GetDeviceForStaticSessionIdDep } from '../getDeviceForStaticSessionId';
import { GetDeviceHasAllowance } from '../getDeviceHasAllowance';
@@ -17,6 +22,7 @@ import { GetDeviceHasAllowance } from '../getDeviceHasAllowance';
export type EnsureQuotaDeps = {
dispatch: Dispatch;
hasAllowance: GetDeviceHasAllowance;
getIsQuotaManagerEnabled: () => boolean;
} & GetDeviceForStaticSessionIdDep;
export type EnsureQuotaParams = {
@@ -28,7 +34,7 @@ export type EnsureQuotaParams = {
export type EnsureQuota = (
params: EnsureQuotaParams,
) => Promise<Result<void, WriteModeRequiredForAllocationErrType>>;
) => Promise<Result<void, WriteModeRequiredForAllocationErrType | QuotaManagerDisabledErrType>>;
export type EnsureQuotaDep = {
ensureQuota: EnsureQuota;
@@ -41,15 +47,18 @@ export const createEnsureQuota =
const device = deps.getDeviceForStaticSessionId(deviceStaticSessionId);
if (!deps.getIsQuotaManagerEnabled()) {
return err(QuotaManagerDisabled());
}
if (
device?.id !== null &&
device?.id !== undefined &&
isNotNullOrUndefined(device?.id) &&
deps.hasAllowance({ walletDescriptor, deviceId: device.id })
) {
return ok(undefined);
}
if (device !== null && isTrezorDeviceWithState(device)) {
if (isNotNull(device) && isTrezorDeviceWithState(device)) {
await deps.dispatch(
ensureDeviceHasQuotaThunk({
device,
@@ -68,7 +77,7 @@ export const createEnsureQuota =
);
if (
allocatedQuota.success === false &&
!allocatedQuota.success &&
allocatedQuota.error.type === 'WriteModeRequiredForAllocation'
) {
return err(WriteModeRequiredForAllocation());

View File

@@ -8,6 +8,7 @@ import type { WriteModeRequiredForAllocationErrType } from '@suite-common/suite-
import { DeviceCancelledErrType, DeviceErrorType } from '@suite-common/suite-types';
import { StaticSessionId } from '@trezor/connect';
import { Result, err, ok } from '@trezor/type-utils';
import { isNotNull } from '@trezor/utils';
import { EnsureQuotaDep } from './createEnsureQuota';
import { createStorageIdFromDeviceStaticSessionId } from './createStorageIdFromDeviceStaticSessionId';
@@ -51,7 +52,7 @@ export const createEnsureStorage =
const storage = deps.suiteSyncStorageRepository.get(storageId);
if (storage !== null) {
if (isNotNull(storage)) {
return ok(storage);
}
@@ -69,6 +70,13 @@ export const createEnsureStorage =
const { owner, delegatedKey } = keysResult.payload;
const relayUrl = deps.getRelayUrl();
const url = isNotNull(relayUrl) && relayUrl.trim() !== '' ? relayUrl : deps.defaultRelayUrl;
const newStorage = deps.createSuiteStorage({
suiteSyncOwner: owner,
});
const quotaResult = await deps.ensureQuota({
deviceStaticSessionId,
delegatedKey,
@@ -76,16 +84,11 @@ export const createEnsureStorage =
isWriteMode,
});
if (!quotaResult.success) {
return quotaResult;
if (quotaResult.success || quotaResult.error.type === 'QuotaManagerDisabled') {
// Only set the relay URL for transport in case that quota manager is enabled or has quota for device.
newStorage.updateRelayUrl(url);
}
const relayUrl = deps.getRelayUrl();
const newStorage = deps.createSuiteStorage({
suiteSyncOwner: owner,
relayUrl: relayUrl !== null && relayUrl.trim() !== '' ? relayUrl : deps.defaultRelayUrl,
});
deps.suiteSyncStorageRepository.set(storageId, newStorage);
return ok(newStorage);

View File

@@ -1,6 +1,7 @@
import { TrezorDevice } from '@suite-common/suite-types';
import { TrezorDevice, TrezorDeviceWithState } from '@suite-common/suite-types';
import { asWalletDescriptor } from '@suite-common/wallet-types';
import { StaticSessionId } from '@trezor/connect';
import { isNotNullOrUndefined } from '@trezor/utils';
export const parseDeviceStaticSessionId = (deviceStaticSessionId: StaticSessionId) => {
const [walletDescriptor, deviceId] = deviceStaticSessionId.split('@');
@@ -14,3 +15,8 @@ export const parseDeviceStaticSessionId = (deviceStaticSessionId: StaticSessionI
// local copy of import { isApprovalFlowSupported } from '@suite-common/device'; > reviewTransactionUtils
export const isApprovalFlowSupported = (device: TrezorDevice | undefined) =>
!device?.unavailableCapabilities?.['evmApproval'];
export const isTrezorDeviceWithState = (
device: TrezorDevice | undefined,
): device is TrezorDeviceWithState =>
isNotNullOrUndefined(device?.id) && isNotNullOrUndefined(device.state?.staticSessionId);