Skip to content

Commit 763589a

Browse files
committed
feat: add storeCredential
1 parent 58945a8 commit 763589a

File tree

3 files changed

+110
-55
lines changed

3 files changed

+110
-55
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { ChainSyncEventType } from "@cardano-sdk/core";
2-
import { CredentialEntity, typeormOperator } from "../../dist/esm";
3-
import { uniq } from "lodash";
4-
import { Mappers } from "@cardano-sdk/projection";
1+
import { Cardano, ChainSyncEventType, TxBodyCBOR } from '@cardano-sdk/core';
2+
import { Hash28ByteBase16 } from '@cardano-sdk/crypto';
3+
import { Mappers } from '@cardano-sdk/projection';
4+
import { credentialsFromAddress } from '@cardano-sdk/projection/src/operators/Mappers/util';
5+
import { CredentialEntity, CredentialType, OutputEntity } from '../entity';
6+
import { typeormOperator } from './util';
57

68
export const storeCredentials = typeormOperator<Mappers.WithUtxo & Mappers.WithAddresses>(async (evt) => {
79
const {
@@ -12,17 +14,65 @@ export const storeCredentials = typeormOperator<Mappers.WithUtxo & Mappers.WithA
1214

1315
// produced credentials will be automatically deleted via block cascade
1416
if (txs.length === 0 || eventType !== ChainSyncEventType.RollForward) return;
15-
16-
const involvedOutputCredentials = uniq(evt.utxo.produced.map(([_, txOut]) => txOut.address).map((address) => {
17-
const credential = credentialsFromAddress(address);
18-
return {
19-
spendingHash: credential.spendingCredentialHash,
20-
stakeHash: credential.stakeCredential,
21-
transactions: [] // FIXME
22-
} as CredentialEntity;
23-
}))
24-
25-
// TODO: compute involvedInputCredentials from HyrdratedTxIn addresses
26-
// TODO: compute involved credentials by merging uniq(input + output) credentials
27-
// TODO: reference transactions
28-
});
17+
18+
const utxoRepository = queryRunner.manager.getRepository(OutputEntity);
19+
const txIdToCredentials = new Map<Cardano.TransactionId, Map<Hash28ByteBase16, CredentialType>>();
20+
21+
const addCredential = (
22+
{ paymentCredentialHash, stakeCredential }: Mappers.Address,
23+
map: Map<Hash28ByteBase16, CredentialType>
24+
) => {
25+
if (paymentCredentialHash) {
26+
map.set(paymentCredentialHash, CredentialType.PAYMENT);
27+
}
28+
if (stakeCredential && !('slot' in stakeCredential)) {
29+
// FIXME: Support pointer stake credentials
30+
map.set(stakeCredential, CredentialType.STAKE);
31+
}
32+
};
33+
34+
// get input & output credentials by tx
35+
for (const tx of evt.block.body) {
36+
const txCredentials = new Map<Hash28ByteBase16, CredentialType>();
37+
38+
// get tx input address
39+
const txInOutputs = await Promise.all(
40+
tx.body.inputs.map(
41+
async ({ txId, index: outputIndex }) =>
42+
await utxoRepository.findOne({ select: { address: true }, where: { outputIndex, txId } })
43+
)
44+
);
45+
46+
// add tx input credentials to involved tx credentials
47+
for (const txOut of txInOutputs) {
48+
if (txOut && txOut.address) {
49+
addCredential(credentialsFromAddress(txOut.address), txCredentials);
50+
}
51+
}
52+
53+
// add tx output credentials to involved tx credentials
54+
for (const txOut of tx.body.outputs) {
55+
addCredential(credentialsFromAddress(txOut.address), txCredentials);
56+
}
57+
58+
const credentialEntities = Array.from(txCredentials).map(
59+
([credentialHash, credentialType]): CredentialEntity => ({
60+
credentialHash: Buffer.from(credentialHash, 'hex'),
61+
credentialType
62+
})
63+
);
64+
65+
await queryRunner.manager
66+
.createQueryBuilder()
67+
.insert()
68+
.into(CredentialEntity)
69+
.values(credentialEntities)
70+
.orIgnore()
71+
.execute();
72+
73+
// store tx credentials for transaction entities
74+
txIdToCredentials.set(tx.id, txCredentials);
75+
}
76+
77+
return { txIdToCredentials };
78+
});

packages/projection/src/operators/Mappers/util.ts

+40
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Asset, Cardano, Handle } from '@cardano-sdk/core';
2+
import { Hash28ByteBase16 } from '@cardano-sdk/crypto';
23
import { Logger } from 'ts-log';
4+
import { Address } from './withAddresses';
35

46
/** Up to 100k transactions per block. Fits in 64-bit signed integer. */
57
export const computeCompactTxId = (blockHeight: number, txIndex: number) => blockHeight * 100_000 + txIndex;
@@ -12,3 +14,41 @@ export const assetNameToUTF8Handle = (assetName: Cardano.AssetName, logger: Logg
1214
}
1315
return handle;
1416
};
17+
18+
export const credentialsFromAddress = (address: Cardano.PaymentAddress): Address => {
19+
const parsed = Cardano.Address.fromString(address)!;
20+
let paymentCredentialHash: Hash28ByteBase16 | undefined;
21+
let stakeCredentialHash: Hash28ByteBase16 | undefined;
22+
let pointer: Cardano.Pointer | undefined;
23+
const type = parsed.getType();
24+
switch (type) {
25+
case Cardano.AddressType.BasePaymentKeyStakeKey:
26+
case Cardano.AddressType.BasePaymentKeyStakeScript:
27+
case Cardano.AddressType.BasePaymentScriptStakeKey:
28+
case Cardano.AddressType.BasePaymentScriptStakeScript: {
29+
const baseAddress = parsed.asBase()!;
30+
paymentCredentialHash = baseAddress.getPaymentCredential().hash;
31+
stakeCredentialHash = baseAddress.getStakeCredential().hash;
32+
break;
33+
}
34+
case Cardano.AddressType.EnterpriseKey:
35+
case Cardano.AddressType.EnterpriseScript: {
36+
const enterpriseAddress = parsed.asEnterprise()!;
37+
paymentCredentialHash = enterpriseAddress.getPaymentCredential().hash;
38+
break;
39+
}
40+
case Cardano.AddressType.PointerKey:
41+
case Cardano.AddressType.PointerScript: {
42+
const pointerAddress = parsed.asPointer()!;
43+
paymentCredentialHash = pointerAddress.getPaymentCredential().hash;
44+
pointer = pointerAddress.getStakePointer();
45+
break;
46+
}
47+
}
48+
return {
49+
address,
50+
paymentCredentialHash,
51+
stakeCredential: stakeCredentialHash || pointer,
52+
type
53+
};
54+
};

packages/projection/src/operators/Mappers/withAddresses.ts

+2-37
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Hash28ByteBase16 } from '@cardano-sdk/crypto';
33
import { WithUtxo } from './withUtxo';
44
import { unifiedProjectorOperator } from '../utils';
55
import uniq from 'lodash/uniq.js';
6+
import { credentialsFromAddress } from './util';
67

78
export interface Address {
89
address: Cardano.PaymentAddress;
@@ -20,41 +21,5 @@ export interface WithAddresses {
2021
/** Collect all unique addresses from produced utxo */
2122
export const withAddresses = unifiedProjectorOperator<WithUtxo, WithAddresses>((evt) => ({
2223
...evt,
23-
addresses: uniq(evt.utxo.produced.map(([_, txOut]) => txOut.address)).map((address): Address => {
24-
const parsed = Cardano.Address.fromString(address)!;
25-
let paymentCredentialHash: Hash28ByteBase16 | undefined;
26-
let stakeCredentialHash: Hash28ByteBase16 | undefined;
27-
let pointer: Cardano.Pointer | undefined;
28-
const type = parsed.getType();
29-
switch (type) {
30-
case Cardano.AddressType.BasePaymentKeyStakeKey:
31-
case Cardano.AddressType.BasePaymentKeyStakeScript:
32-
case Cardano.AddressType.BasePaymentScriptStakeKey:
33-
case Cardano.AddressType.BasePaymentScriptStakeScript: {
34-
const baseAddress = parsed.asBase()!;
35-
paymentCredentialHash = baseAddress.getPaymentCredential().hash;
36-
stakeCredentialHash = baseAddress.getStakeCredential().hash;
37-
break;
38-
}
39-
case Cardano.AddressType.EnterpriseKey:
40-
case Cardano.AddressType.EnterpriseScript: {
41-
const enterpriseAddress = parsed.asEnterprise()!;
42-
paymentCredentialHash = enterpriseAddress.getPaymentCredential().hash;
43-
break;
44-
}
45-
case Cardano.AddressType.PointerKey:
46-
case Cardano.AddressType.PointerScript: {
47-
const pointerAddress = parsed.asPointer()!;
48-
paymentCredentialHash = pointerAddress.getPaymentCredential().hash;
49-
pointer = pointerAddress.getStakePointer();
50-
break;
51-
}
52-
}
53-
return {
54-
address,
55-
paymentCredentialHash,
56-
stakeCredential: stakeCredentialHash || pointer,
57-
type
58-
};
59-
})
24+
addresses: uniq(evt.utxo.produced.map(([_, txOut]) => txOut.address)).map(credentialsFromAddress)
6025
}));

0 commit comments

Comments
 (0)