@ocap/types
Version:
Typescript definitions generated from protobuf
558 lines (495 loc) • 16.7 kB
text/typescript
// 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;