Skip to content

Commit

Permalink
feat(tx-projection): add projection for transaction projected indexed…
Browse files Browse the repository at this point in the history
… by credentials
  • Loading branch information
will-break-it committed Jul 24, 2024
1 parent 9d68d1b commit e05dec9
Show file tree
Hide file tree
Showing 16 changed files with 534 additions and 114 deletions.
3 changes: 2 additions & 1 deletion compose/common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ services:
'--node-config',
'/config/cardano-node/config.json',
'--log-level-websocket',
'Warning'
'Warning',
'--include-cbor'
]
depends_on:
cardano-node:
Expand Down
45 changes: 13 additions & 32 deletions demo/projection-typeorm.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,14 @@ const {
withTypeormTransaction,
typeormTransactionCommit,
TypeormStabilityWindowBuffer,
BlockDataEntity,
BlockEntity,
StakePoolEntity,
PoolRegistrationEntity,
PoolRetirementEntity,
OutputEntity,
AssetEntity,
TokensEntity,
storeBlock,
storeAssets,
BlockEntity,
CredentialEntity,
TransactionEntity,
storeUtxo,
storeStakePools,
storeStakePoolMetadataJob,
storeBlock,
storeCredentials,
storeTransactions,
isRecoverableTypeormError
} = require('@cardano-sdk/projection-typeorm');
const { OgmiosObservableCardanoNode } = require('@cardano-sdk/ogmios');
Expand All @@ -32,14 +27,9 @@ const logger = {
};

const entities = [
BlockDataEntity,
BlockEntity,
StakePoolEntity,
PoolRegistrationEntity,
PoolRetirementEntity,
AssetEntity,
TokensEntity,
OutputEntity
CredentialEntity,
TransactionEntity,
];
const extensions = {
pgBoss: true
Expand Down Expand Up @@ -101,26 +91,17 @@ Bootstrap.fromCardanoNode({
logger
})
.pipe(
Mappers.withCertificates(),
Mappers.withStakePools(),
Mappers.withMint(),
Mappers.withUtxo(),
// Single-tenant example
// Mappers.filterProducedUtxoByAddresses({
// addresses: [
// 'addr_test1qpgn04xka0857kh6859za75tfvlrlu2lft0yc9z87598yjezw8yvpkv977yj5va20xmd9vw5fczfl3uu4expskz8adfqpydths'
// ]
// }),
Mappers.withAddresses(),
shareRetryBackoff(
(evt$) =>
evt$.pipe(
withTypeormTransaction({ dataSource$, logger }, extensions),
storeBlock(),
buffer.storeBlockData(),
storeAssets(),
storeUtxo(),
storeStakePools(),
storeStakePoolMetadataJob(),
storeBlock(),
storeCredentials(),
storeTransactions(),
// buffer.storeBlockData(),
typeormTransactionCommit()
),
{ shouldRetry: isRecoverableTypeormError }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
AssetEntity,
BlockDataEntity,
BlockEntity,
CredentialEntity,
CurrentPoolMetricsEntity,
DataSourceExtensions,
HandleEntity,
Expand All @@ -17,28 +18,33 @@ import {
StakeKeyRegistrationEntity,
StakePoolEntity,
TokensEntity,
TransactionEntity,
createStorePoolMetricsUpdateJob,
createStoreStakePoolMetadataJob,
storeAddresses,
storeAssets,
storeBlock,
storeCredentials,
storeHandleMetadata,
storeHandles,
storeNftMetadata,
storeStakeKeyRegistrations,
storeStakePoolRewardsJob,
storeStakePools,
storeTransactions,
storeUtxo,
willStoreAddresses,
willStoreAssets,
willStoreBlockData,
willStoreCredentials,
willStoreHandleMetadata,
willStoreHandles,
willStoreNftMetadata,
willStoreStakeKeyRegistrations,
willStoreStakePoolMetadataJob,
willStoreStakePoolRewardsJob,
willStoreStakePools,
willStoreTransactions,
willStoreUtxo
} from '@cardano-sdk/projection-typeorm';
import { Cardano, ChainSyncEventType } from '@cardano-sdk/core';
Expand All @@ -57,6 +63,7 @@ export enum ProjectionName {
StakePoolMetadataJob = 'stake-pool-metadata-job',
StakePoolMetricsJob = 'stake-pool-metrics-job',
StakePoolRewardsJob = 'stake-pool-rewards-job',
Transaction = 'transaction',
UTXO = 'utxo'
}

Expand Down Expand Up @@ -112,6 +119,7 @@ export const storeOperators = {
storeAddresses: storeAddresses(),
storeAssets: storeAssets(),
storeBlock: storeBlock(),
storeCredentials: storeCredentials(),
storeHandleMetadata: storeHandleMetadata(),
storeHandles: storeHandles(),
storeNftMetadata: storeNftMetadata(),
Expand All @@ -123,6 +131,7 @@ export const storeOperators = {
storeStakePoolMetadataJob: createStoreStakePoolMetadataJob()(),
storeStakePoolRewardsJob: storeStakePoolRewardsJob(),
storeStakePools: storeStakePools(),
storeTransactions: storeTransactions(),
storeUtxo: storeUtxo()
};
type StoreOperators = typeof storeOperators;
Expand All @@ -138,13 +147,15 @@ type WillStore = {
const willStore: Partial<WillStore> = {
storeAddresses: willStoreAddresses,
storeAssets: willStoreAssets,
storeCredentials: willStoreCredentials,
storeHandleMetadata: willStoreHandleMetadata,
storeHandles: willStoreHandles,
storeNftMetadata: willStoreNftMetadata,
storeStakeKeyRegistrations: willStoreStakeKeyRegistrations,
storeStakePoolMetadataJob: willStoreStakePoolMetadataJob,
storeStakePoolRewardsJob: willStoreStakePoolRewardsJob,
storeStakePools: willStoreStakePools,
storeTransactions: willStoreTransactions,
storeUtxo: willStoreUtxo
};

Expand All @@ -153,6 +164,7 @@ const entities = {
asset: AssetEntity,
block: BlockEntity,
blockData: BlockDataEntity,
credential: CredentialEntity,
currentPoolMetrics: CurrentPoolMetricsEntity,
handle: HandleEntity,
handleMetadata: HandleMetadataEntity,
Expand All @@ -165,7 +177,8 @@ const entities = {
poolRewards: PoolRewardsEntity,
stakeKeyRegistration: StakeKeyRegistrationEntity,
stakePool: StakePoolEntity,
tokens: TokensEntity
tokens: TokensEntity,
transaction: TransactionEntity
};
export const allEntities = Object.values(entities);
type Entities = typeof entities;
Expand All @@ -176,6 +189,7 @@ const storeEntities: Partial<Record<StoreName, EntityName[]>> = {
storeAddresses: ['address'],
storeAssets: ['asset'],
storeBlock: ['block', 'blockData'],
storeCredentials: ['credential', 'transaction', 'output'],
storeHandleMetadata: ['handleMetadata', 'output'],
storeHandles: ['handle', 'asset', 'tokens', 'output'],
storeNftMetadata: ['asset'],
Expand All @@ -186,13 +200,15 @@ const storeEntities: Partial<Record<StoreName, EntityName[]>> = {
storeStakePoolMetadataJob: ['stakePool', 'currentPoolMetrics', 'poolMetadata'],
storeStakePoolRewardsJob: ['poolRewards', 'stakePool'],
storeStakePools: ['stakePool', 'currentPoolMetrics', 'poolMetadata', 'poolDelisted'],
storeTransactions: ['block', 'transaction'],
storeUtxo: ['tokens', 'output']
};

const entityInterDependencies: Partial<Record<EntityName, EntityName[]>> = {
address: ['stakeKeyRegistration'],
asset: ['block', 'nftMetadata'],
blockData: ['block'],
credential: [],
currentPoolMetrics: ['stakePool'],
handle: ['asset'],
handleMetadata: ['output'],
Expand All @@ -203,7 +219,8 @@ const entityInterDependencies: Partial<Record<EntityName, EntityName[]>> = {
poolRetirement: ['block'],
stakeKeyRegistration: ['block'],
stakePool: ['block', 'poolRegistration', 'poolRetirement'],
tokens: ['asset']
tokens: ['asset'],
transaction: ['block', 'credential']
};

export const getEntities = (entityNames: EntityName[]): Entity[] => {
Expand Down Expand Up @@ -241,6 +258,7 @@ const mapperInterDependencies: Partial<Record<MapperName, MapperName[]>> = {
const storeMapperDependencies: Partial<Record<StoreName, MapperName[]>> = {
storeAddresses: ['withAddresses'],
storeAssets: ['withMint'],
storeCredentials: ['withAddresses', 'withCertificates', 'withUtxo'],
storeHandleMetadata: ['withHandleMetadata'],
storeHandles: ['withHandles'],
storeNftMetadata: ['withNftMetadata'],
Expand All @@ -260,6 +278,7 @@ const storeInterDependencies: Partial<Record<StoreName, StoreName[]>> = {
storeStakePoolMetadataJob: ['storeBlock'],
storeStakePoolRewardsJob: ['storeBlock'],
storeStakePools: ['storeBlock'],
storeTransactions: ['storeCredentials', 'storeBlock'],
storeUtxo: ['storeBlock', 'storeAssets']
};

Expand All @@ -273,6 +292,7 @@ const projectionStoreDependencies: Record<ProjectionName, StoreName[]> = {
'stake-pool-metadata-job': ['storeStakePoolMetadataJob'],
'stake-pool-metrics-job': ['storePoolMetricsUpdateJob'],
'stake-pool-rewards-job': ['storeStakePoolRewardsJob'],
transaction: ['storeCredentials', 'storeTransactions', 'storeUtxo'],
utxo: ['storeUtxo']
};

Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/Cardano/types/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ProposalProcedure, VotingProcedures } from './Governance';
import { RewardAccount } from '../Address';
import { Script } from './Script';
import { TxBodyCBOR } from '../../CBOR/TxBodyCBOR';
import { TxCBOR } from '../../CBOR';
import { bytesToHex, hexToBytes } from '../../util/misc';

/** transaction hash as hex string */
Expand Down Expand Up @@ -159,6 +160,7 @@ export interface OnChainTx<TBody extends TxBody = TxBody>
extends Omit<TxWithInputSource<TBody>, 'witness' | 'auxiliaryData'> {
witness: Omit<Witness, 'scripts'>;
auxiliaryData?: Omit<AuxiliaryData, 'scripts'>;
cbor?: TxCBOR;
}

export interface HydratedTx extends TxWithInputSource<HydratedTxBody> {
Expand Down
4 changes: 3 additions & 1 deletion packages/ogmios/src/ogmiosToCore/tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
NotImplementedError,
Serialization,
SerializationError,
SerializationFailure
SerializationFailure,
TxCBOR
} from '@cardano-sdk/core';
import { CommonBlock } from './types';
import { Schema } from '@cardano-ogmios/client';
Expand Down Expand Up @@ -432,6 +433,7 @@ const mapCommonTx = (tx: Schema.Transaction): Cardano.OnChainTx => {
withdrawals: mapWithdrawals(tx.withdrawals)
})
},
cbor: tx.cbor ? TxCBOR(tx.cbor) : undefined,
id: Cardano.TransactionId(tx.id),
// At the time of writing Byron transactions didn't set this property
inputSource: tx.spends ? Cardano.InputSource[tx.spends] : Cardano.InputSource.inputs,
Expand Down
26 changes: 26 additions & 0 deletions packages/projection-typeorm/src/entity/Credential.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Column, Entity, Index, ManyToMany, PrimaryColumn } from 'typeorm';
import { Hash28ByteBase16 } from '@cardano-sdk/crypto';
import { TransactionEntity } from './Transaction.entity';

export enum CredentialType {
PaymentKey = 'payment_key',
PaymentScript = 'payment_script',
StakeKey = 'stake_key',
StakeScript = 'stake_script'
}

@Entity()
export class CredentialEntity {
@Index()
@PrimaryColumn('varchar')
credentialHash?: Hash28ByteBase16;

@Column('enum', { enum: CredentialType, nullable: false })
credentialType?: CredentialType;

@ManyToMany(() => TransactionEntity, (transaction) => transaction.credentials, { onDelete: 'CASCADE' })
transactions?: TransactionEntity[];
}

export const credentialEntityComparator = (c1: CredentialEntity, c2: CredentialEntity) =>
c1.credentialHash === c2.credentialHash && c1.credentialType === c2.credentialType;
27 changes: 27 additions & 0 deletions packages/projection-typeorm/src/entity/Transaction.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { BlockEntity } from './Block.entity';
import { Cardano, TxCBOR } from '@cardano-sdk/core';
import { Column, Entity, Index, JoinColumn, JoinTable, ManyToMany, ManyToOne, PrimaryColumn } from 'typeorm';
import { CredentialEntity } from './Credential.entity';
import { OnDeleteCascadeRelationOptions } from './util';

@Entity()
export class TransactionEntity {
@Index()
@PrimaryColumn('varchar')
txId?: Cardano.TransactionId;

@Column('varchar', { nullable: false })
cbor?: TxCBOR;

@ManyToOne(() => BlockEntity, OnDeleteCascadeRelationOptions)
@JoinColumn({ name: 'block_id' })
block?: BlockEntity;

@ManyToMany(() => CredentialEntity, (credential) => credential.transactions, { onDelete: 'CASCADE' })
@JoinTable({
inverseJoinColumn: { name: 'credential_id', referencedColumnName: 'credentialHash' },
joinColumn: { name: 'transaction_id', referencedColumnName: 'txId' },
name: 'transaction_credentials'
})
credentials?: CredentialEntity[];
}
2 changes: 2 additions & 0 deletions packages/projection-typeorm/src/entity/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './Address.entity';
export * from './Asset.entity';
export * from './Block.entity';
export * from './BlockData.entity';
export * from './Credential.entity';
export * from './CurrentPoolMetrics.entity';
export * from './Handle.entity';
export * from './HandleMetadata.entity';
Expand All @@ -15,4 +16,5 @@ export * from './PoolRewards.entity';
export * from './StakeKey.entity';
export * from './StakeKeyRegistration.entity';
export * from './StakePool.entity';
export * from './Transaction.entity';
export * from './Tokens.entity';
2 changes: 2 additions & 0 deletions packages/projection-typeorm/src/operators/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './storeAddresses';
export * from './storeAssets';
export * from './storeBlock';
export * from './storeCredentials';
export * from './storeHandles';
export * from './storeHandleMetadata';
export * from './storeNftMetadata';
Expand All @@ -10,6 +11,7 @@ export * from './storeStakeKeyRegistrations';
export * from './storeStakePools';
export * from './storeStakePoolMetadataJob';
export * from './storeStakePoolRewardsJob';
export * from './storeTransactions';
export * from './storeUtxo';
export * from './util';
export * from './withTypeormTransaction';
Loading

0 comments on commit e05dec9

Please sign in to comment.