UNPKG

@ocap/types

Version:
558 lines (495 loc) 16.7 kB
// core/types/interfaces/pipeline.ts // // Transaction Pipeline Context Type System // ======================================== // // This module defines a 5-phase context type hierarchy for transaction processing: // // Phase 1: IBaseContext -> Initial input (txBase64, statedb, config, indexdb) // Phase 2: IDecodedContext -> After DecodeTx + DecodeItx (tx, itx, txType, txHash) // Phase 3: IGasContext -> After ExtractState + EnsureTxGas + EnsureTxCost // Phase 4: IReadyContext -> After TakeStateSnapshot, ready for business logic // Phase 5: IExecutedContext -> After business logic execution complete // // Protocol-specific states are provided as Mixin interfaces (IWithSender, IWithAsset, etc.) // that can be combined with phase types to create protocol-specific context types. // // Example: // type DelegateContext = IReadyContext<TDelegateTx> & IWithSender & IWithReceiver & IWithDelegation; // import type { ILedger } from './ledger'; import type { TTokenInput, TTransactionReceipt } from '../lib/type_pb'; import type { BN } from './base'; import type { IAny } from './base'; import type { IChainConfig } from './config'; import type { IIndexDB } from './indexdb'; import type { IAccountState, IAssetFactoryState, IAssetState, IDelegateState, IEvidenceState, IRollupBlock, IRollupState, IStakeState, ITokenFactoryState, ITokenState, TokenBalanceMap, } from './state'; import type { IStateDB } from './statedb'; // ============================================================================= // Common Interfaces // ============================================================================= /** Logger interface for pipeline operations */ export interface IPipelineLogger { /** Logger methods accept any arguments for flexible logging */ debug: (message: string, ...args: unknown[]) => void; info: (message: string, ...args: unknown[]) => void; warn: (message: string, ...args: unknown[]) => void; error: (message: string, ...args: unknown[]) => void; } /** * Decoded transaction — canonical camelCase shape (Phase 4 decision q11). * * Repeated fields use the `xxx` name (`signatures`, `tags`, `slashers`). The * protobuf-wire `xxxList` alias is an `@ocap/proto/runtime` implementation * detail and is not part of this interface. Pipe code still accessing * `tx.signaturesList` at runtime (protobuf-decoded txs) falls through the * index signature as `unknown`; prefer `getListField(tx, 'signatures')` to * read either shape safely. */ export interface IDecodedTransaction { from: string; delegator?: string; chainId: string; nonce: number; serviceFee?: string; pk: Uint8Array; signature?: Uint8Array | string; signatures?: IMultiSigData[]; itx: IAny; /** Index signature for dynamic field access during verification */ [key: string]: unknown; } /** Multi-signature data structure for pipeline (decoded format, not proto) */ export interface IMultiSigData { signer: string; delegator?: string; pk: Uint8Array; signature: Uint8Array; data?: IAny; } /** Account update record */ export interface IAccountUpdate { address: string; delta?: string; token?: string; action?: string; } /** Locked state for stake verification */ export interface ILockedState { address: string; tokens: TokenBalanceMap; assets: string[]; } /** Input change tracking for token factory operations */ export interface IInputChange { address: string; token?: string; delta: string; } /** Validated inner transaction for delegation */ export interface IValidatedItx { opsList?: Array<{ typeUrl?: string; limit?: { tokensList?: Array<{ address?: string; txCount?: number; txAllowance?: string; totalAllowance?: string; validUntil?: number; rate?: { interval?: number; anchor?: number } | null; }>; assetsList?: Array<{ address?: string[]; txCount?: number; validUntil?: number; rate?: { interval?: number; anchor?: number } | null; }>; }; }>; } /** Gas stake configuration */ export interface IGasStake { owner?: string; stakeId?: string; state?: IStakeState | null; valid?: boolean; } /** Token condition for balance verification */ export interface ITokenCondition { owner: string; tokens: Array<{ address: string; value: BN }>; } /** Output structure for transaction results */ export interface IOutput { owner: string; tokens?: TTokenInput[]; assets?: string[]; } /** Input structure for transaction */ export interface IInput { owner: string; tokensList?: TTokenInput[]; assetsList?: string[]; } // ============================================================================= // Phase 1: Base Context // ============================================================================= /** * Phase 1: Initial input context * * This is the starting point of the pipeline. Contains only the required * inputs provided by the caller before any processing begins. * * @template TItx - Inner transaction type (default: unknown) */ export interface IBaseContext<TItx = unknown> { /** Base64 encoded transaction */ txBase64: string; /** * Wire encoding detected by `DecodeTx` from the first three bytes of the * decoded payload. Downstream pipes (verify-signature, verify-multisig, * and tx-protocols' `toItxAddress` call sites) must select the matching * serializer. Literal union (not `string`) to catch typos at compile time. * * Populated by DecodeTx on Phase 1+; optional on `IBaseContext` so the * pipeline can still type-check callers that build a bare base context * (e.g. test harnesses that exercise DecodeTx first). */ txEncoding?: 'cbor' | 'protobuf'; /** State database instance */ statedb: IStateDB; /** Chain configuration */ config: IChainConfig; /** Index database instance */ indexdb: IIndexDB; /** Optional logger for pipeline operations */ logger?: IPipelineLogger; /** Extra context data */ extra?: { /** Gas stake headers from HTTP request */ gasStakeHeaders?: { token?: string; pk?: string }; /** HTTP request object - format varies by server framework */ request?: unknown; /** Extension point for protocol-specific data */ [key: string]: unknown; }; // Internal fields that may be set at any phase /** Database transaction handle - type depends on StateDB implementation */ txn?: unknown; /** State helper functions - set by execute.ts */ states?: unknown; /** Event queue for indexdb updates */ events?: Array<{ table: string; name: string; data: unknown; ctx: Record<string, unknown> }>; // Type hint for TItx (not used at runtime) /** @internal Type parameter placeholder */ readonly __itxType?: TItx; /** Verifiable ledger instance for append-only transaction log */ ledger: ILedger; /** Ledger sequence number (set by WriteLedger pipe, used in Phase 6) */ ledgerSequence?: number; /** Index signature for IOperationContext compatibility and dynamic property access */ [key: string]: unknown; } // ============================================================================= // Phase 1.5: Tx Only Context (intermediate state) // ============================================================================= /** * Intermediate context after DecodeTx but before DecodeItx * * At this phase, the outer transaction has been decoded but the inner * transaction (itx) has not been extracted yet. Used by DecodeItx pipe. * * Added by: DecodeTx pipe */ export interface ITxOnlyContext extends IBaseContext { /** Decoded transaction object (itx is still encoded as Any) */ tx: IDecodedTransaction; /** Transaction hash (DID format) */ txHash: string; /** Transaction timestamp (ISO 8601) */ txTime: string; /** Transaction size in bytes */ txSize: number; /** Whether to charge base gas */ txBaseGas: boolean; /** * Wire encoding of the transaction. Always populated by `DecodeTx` before * any downstream pipe runs, hence required here and inherited by the * decoded / gas / ready / executed phases via the extends chain. */ txEncoding: 'cbor' | 'protobuf'; } // ============================================================================= // Phase 2: Decoded Context // ============================================================================= /** * Phase 2: Context after DecodeTx + DecodeItx * * At this phase, the transaction has been decoded from base64 and the inner * transaction (itx) has been extracted. The transaction can now be routed * to the appropriate protocol handler. * * Added by: DecodeTx, DecodeItx pipes * * @template TItx - Inner transaction type */ export interface IDecodedContext<TItx = unknown> extends ITxOnlyContext { /** Decoded inner transaction */ itx: TItx; /** Transaction type URL (e.g., 'fg:t:transfer') */ txType: string; } // ============================================================================= // Phase 3: Gas Context // ============================================================================= /** * Phase 3: Context after Gas calculation * * At this phase, states have been extracted (ExtractState) and gas/cost * calculations are complete (EnsureTxGas, EnsureTxCost). The transaction * fees have been calculated and sender updates prepared. * * Added by: ExtractState, EnsureTxGas, EnsureTxCost pipes * * @template TItx - Inner transaction type */ export interface IGasContext<TItx = unknown> extends IDecodedContext<TItx> { /** Total gas consumed (set by EnsureGas pipe) */ totalGas?: BN; /** Gas estimate breakdown (set by EnsureGas pipe) */ gasEstimate?: { create: number; update: number; payment?: number }; /** Receipts for balance changes */ receipts?: TTransactionReceipt[]; // Fee calculation results /** Pending updates to apply to sender account */ senderUpdates?: Record<string, string>; /** Function to update vault balances */ updateVaults?: () => Promise<void> | void; /** Sender balance change record */ senderChange?: { address: string; token: string; delta: string } | null; // Vault configuration (optional - depends on chain config) /** Gas vault address */ gasVault?: string; /** Fee vault address */ feeVault?: string; /** Gas stake configuration */ gasStake?: IGasStake; /** Fee vault change record */ feeVaultChange?: { address: string; value: string }; /** Gas vault change record */ gasVaultChange?: { address: string; value: string }; // Flags /** Whether gas has been paid */ gasPaid?: boolean; /** Whether using gas stake */ isGasStake?: boolean; // Account updates (populated by updateVaults callback) /** List of account updates applied */ updatedAccounts?: IAccountUpdate[]; } // ============================================================================= // Phase 4: Ready Context // ============================================================================= /** * Phase 4: Context ready for business logic execution * * At this phase, all preparation is complete: * - Transaction decoded * - States extracted * - Gas calculated * - State snapshot taken * * This is the input type for protocol-specific business logic pipes. * Combine with Mixin interfaces to create protocol-specific context types. * * Added by: TakeStateSnapshot pipe * * @template TItx - Inner transaction type * * @example * ```typescript * // Define protocol-specific context * type DelegateContext = IReadyContext<TDelegateTx> * & IWithSender * & IWithReceiver * & Partial<IWithDelegation>; * * // Use in business logic pipe * runner.use(async (context: DelegateContext, next) => { * const { senderState, receiverState, delegationState } = context; * // ... business logic * }); * ``` */ export interface IReadyContext<TItx = unknown> extends IGasContext<TItx> { // Vault states (extracted by TakeStateSnapshot if configured) /** Fee vault account state */ feeVaultState?: IAccountState; /** Gas vault account state */ gasVaultState?: IAccountState; } // ============================================================================= // Phase 5: Executed Context // ============================================================================= /** * Phase 5: Context after business logic execution * * At this phase, the business logic has completed and all state * modifications have been applied. Ready for final verification * (VerifyStateDiff) and transaction persistence. * * @template TItx - Inner transaction type */ export interface IExecutedContext<TItx = unknown> extends IReadyContext<TItx> { /** List of account updates applied - required at this phase */ updatedAccounts: IAccountUpdate[]; } // ============================================================================= // Mixin Interfaces: Account States // ============================================================================= /** Mixin: Sender account state is required */ export interface IWithSender { senderState: IAccountState; } /** Mixin: Receiver account state is required */ export interface IWithReceiver { receiverState: IAccountState; } /** Mixin: Delegator account state is required */ export interface IWithDelegator { delegatorState: IAccountState; } /** Mixin: Multiple delegator account states are required */ export interface IWithDelegators { delegatorStates: IAccountState[]; } /** Mixin: Owner account state is required */ export interface IWithOwner { ownerState: IAccountState; ownerAddress: string; } /** Mixin: Issuer account state is required */ export interface IWithIssuer { issuerState: IAccountState; } /** Mixin: Multiple issuer account states are required */ export interface IWithIssuers { issuerStates: IAccountState[]; } /** Mixin: Locker account state is required */ export interface IWithLocker { lockerState: IAccountState; lockerAddress: string; } /** Mixin: Multiple signer states are required */ export interface IWithSigners { signerStates: IAccountState[]; signers: string[]; } /** Mixin: Multiple receiver states are required */ export interface IWithReceivers { receiverStates: IAccountState[]; receivers: string[]; } /** Mixin: Multiple sender states are required */ export interface IWithSenders { senderStates: IAccountState[]; senders: string[]; } // ============================================================================= // Mixin Interfaces: Protocol States // ============================================================================= /** Mixin: Asset state is required */ export interface IWithAsset { assetState: IAssetState; } /** Mixin: Multiple asset states are required */ export interface IWithAssets { assetStates: IAssetState[]; } /** Mixin: Token state is required */ export interface IWithToken { tokenState: ITokenState; tokenAddress: string; } /** Mixin: Multiple token states are required */ export interface IWithTokens { tokenStates: ITokenState[]; } /** Mixin: Asset factory state is required */ export interface IWithFactory { factoryState: IAssetFactoryState; } /** Mixin: Token factory state is required */ export interface IWithTokenFactory { tokenFactoryState: ITokenFactoryState; } /** Mixin: Stake state is required */ export interface IWithStake { stakeState: IStakeState; stakeAddress: string; } /** Mixin: Multiple stake states are required */ export interface IWithStakes { stakeStates: IStakeState[]; } /** Mixin: Delegation state is required */ export interface IWithDelegation { delegationState: IDelegateState; } /** Mixin: Rollup state is required */ export interface IWithRollup { rollupState: IRollupState; } /** Mixin: Rollup block state is required */ export interface IWithRollupBlock { rollupBlockState: IRollupState; blockState: IRollupBlock; } /** Mixin: Evidence state is required */ export interface IWithEvidence { evidenceState: IEvidenceState; } /** Mixin: Locked state is required (for stake operations) */ export interface IWithLocked { lockedState: ILockedState; } /** FinalizedContext uses unknown for TItx as it represents any possible inner transaction type */ export type FinalizedContext = IExecutedContext<unknown> & IWithSender & IWithSenders & IWithSigners & IWithReceiver & IWithReceivers & IWithDelegator & IWithDelegators & IWithOwner & IWithIssuer & IWithIssuers & IWithLocker & IWithDelegation & IWithAsset & IWithAssets & IWithToken & IWithTokens & IWithFactory & IWithTokenFactory & IWithStake & IWithStakes & IWithRollup & IWithRollupBlock & IWithEvidence & IWithLocked;