Skip to content

Commit

Permalink
Merge pull request #1467 from input-output-hk/fix/LW-11425-misc-fixes
Browse files Browse the repository at this point in the history
LW-11425 Misc fixes
  • Loading branch information
iccicci authored Sep 11, 2024
2 parents 5189b1e + 764ec3d commit 7c2d694
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 55 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,18 @@ for you.
nix run .#config-update
```

## Get CBOR representation of an on chain transaction

Once we have a [running network](packages/cardano-services/README.md#production) synced at least up to the block
containing the transaction we are interested in, issue following command to get the CBOR representation of the
transaction.

```
yarn tx-cbor <txId>
```

This works regardless of the local ports configuration through environment variables.

## Attic

Previously supported features, no longer supported, but packed with a reference branch.
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
"test": "yarn workspaces foreach -v run test",
"test:build:verify": "yarn workspaces foreach -v run test:build:verify",
"test:e2e": "yarn workspaces foreach -v run test:e2e",
"test:debug": "DEBUG=true yarn workspaces foreach -v run test"
"test:debug": "DEBUG=true yarn workspaces foreach -v run test",
"tx-cbor": "tsx packages/cardano-services/scripts/tx-cbor.js"
},
"repository": {
"type": "git",
Expand Down
105 changes: 105 additions & 0 deletions packages/cardano-services/scripts/tx-cbor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { Pool } from 'pg';
import { readFileSync } from 'fs';
import { spawnSync } from 'child_process';
import WebSocket from 'ws';

interface Tx {
cbor: string;
id: string;
}

const txId = process.argv[2];

const normalizeError = (status: number | null, stderr: string) =>
[status || 1, stderr || 'Unknown error\n', ''] as const;

const inside = async () => {
const db = new Pool({
database: readFileSync(process.env.POSTGRES_DB_FILE_DB_SYNC!).toString(),
host: 'postgres',
password: readFileSync(process.env.POSTGRES_PASSWORD_FILE_DB_SYNC!).toString(),
user: readFileSync(process.env.POSTGRES_USER_FILE_DB_SYNC!).toString()
});

const query = `\
SELECT ENCODE(b2.hash, 'hex') AS id, b2.slot_no::INTEGER AS slot FROM tx
JOIN block b1 ON block_id = b1.id
JOIN block b2 ON b1.previous_id = b2.id
WHERE tx.hash = $1`;

const { rows } = await db.query(query, [Buffer.from(txId, 'hex')]);
const [prevBlock] = rows;

await db.end();

if (!prevBlock) return [1, `Unknown transaction id ${txId}\n`, ''] as const;

const cbor = await new Promise<string>((resolve) => {
const client = new WebSocket(process.env.OGMIOS_URL!);
let request = 0;

const rpc = (method: string, params: unknown) =>
client.send(JSON.stringify({ id: ++request, jsonrpc: '2.0', method, params }));

client.on('open', () => rpc('findIntersection', { points: [prevBlock] }));

client.on('message', (msg) => {
const { result } = JSON.parse(msg.toString()) as { result: { block: { transactions: Tx[] } } };
let tx: Tx | undefined;

if (
result &&
result.block &&
result.block.transactions &&
(tx = result.block.transactions.find((t) => t.id === txId))
) {
client.on('close', () => resolve(tx!.cbor));
client.close();
} else rpc('nextBlock', {});
});
});

return [0, '', `${cbor}\n`] as const;
};

const outside = async () => {
if (!txId) return [1, 'Missing input transaction id\n', ''] as const;

let { status, stderr, stdout } = spawnSync('docker', ['ps'], { encoding: 'utf-8' });

if (status || stderr) return normalizeError(status, stderr);

const container = [
'cardano-services-mainnet-provider-server-1',
'cardano-services-preprod-provider-server-1',
'cardano-services-preview-provider-server-1',
'cardano-services-sanchonet-provider-server-1',
'local-network-e2e-provider-server-1'
].find((name) => stdout.includes(name));

if (!container) return [1, "Can't find any valid container\n", ''] as const;

({ status, stderr, stdout } = spawnSync(
'docker',
['container', 'exec', '-i', container, 'bash', '-c', `cd /app ; INSIDE_THE_CONTAINER=true yarn tx-cbor ${txId}`],
{ encoding: 'utf-8' }
));

if (status || stderr) return normalizeError(status, stderr);

return [0, '', stdout] as const;
};

(process.env.INSIDE_THE_CONTAINER ? inside() : outside())
.then(([status, stderr, stdout]) => {
if (status) {
process.stderr.write(stderr);
// eslint-disable-next-line unicorn/no-process-exit
process.exit(status);
}

process.stdout.write(stdout);
})
.catch((error) => {
throw error;
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// cSpell:ignore descr

import * as Queries from './queries';
import {
AuthorizeCommitteeHotCertModel,
Expand Down Expand Up @@ -261,7 +263,10 @@ export class ChainHistoryBuilder {
if (result.rows.length === 0) return [];

const txOutIds = result.rows.flatMap((txOut) => BigInt(txOut.id));
const multiAssets = await this.queryMultiAssetsByTxOut(txOutIds);
// In case of collateralReturn requests (collateral = true) assets in the output can't be read as for regular outputs:
// db-sync stores assets from collateral outputs in collateral_tx_out.multi_assets_descr column rather than in
// ma_tx_out table like for regular outputs. To have a complete collateralReturn, given column should be read and parsed.
const multiAssets = collateral ? new Map() : await this.queryMultiAssetsByTxOut(txOutIds);
const referenceScripts = await this.queryReferenceScriptsByTxOut(result.rows);

return result.rows.map((txOut) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,10 @@ export class DbSyncChainHistoryProvider extends DbSyncProvider() implements Chai

return txResults.rows.map((tx) => {
const txId = tx.id.toString('hex') as unknown as Cardano.TransactionId;
const txInputs = orderBy(inputs.filter((input) => input.txInputId === txId).map(mapTxIn), ['index']);
const txCollaterals = orderBy(collaterals.filter((col) => col.txInputId === txId).map(mapTxIn), ['index']);
const txInputs = inputs.filter((input) => input.txInputId === txId).map(mapTxIn);
const txCollaterals = collaterals.filter((col) => col.txInputId === txId).map(mapTxIn);
const txOutputs = orderBy(outputs.filter((output) => output.txId === txId).map(mapTxOut), ['index']);
const txCollateralOutputs = orderBy(collateralOutputs.filter((output) => output.txId === txId).map(mapTxOut), [
'index'
]);
const txCollateralOutputs = collateralOutputs.filter((output) => output.txId === txId).map(mapTxOut);
const inputSource: Cardano.InputSource = tx.valid_contract
? Cardano.InputSource.inputs
: Cardano.InputSource.collaterals;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
BlockOutputModel,
CertificateModel,
MultiAssetModel,
PoolRegisterCertModel,
ProtocolParametersUpdateModel,
RedeemerModel,
ScriptModel,
Expand Down Expand Up @@ -191,6 +192,17 @@ const mapDrepDelegation = ({
__typename: 'AlwaysAbstain'
};

const mapPoolParameters = (certModel: WithCertType<PoolRegisterCertModel>): Cardano.PoolParameters => ({
cost: BigInt(certModel.fixed_cost),
id: certModel.pool_id as unknown as Cardano.PoolId,
margin: Cardano.FractionUtils.toFraction(certModel.margin),
owners: [],
pledge: BigInt(certModel.pledge),
relays: [],
rewardAccount: certModel.reward_account as Cardano.RewardAccount,
vrf: certModel.vrf_key_hash.toString('hex') as Cardano.VrfVkHex
});

// eslint-disable-next-line complexity
export const mapCertificate = (
certModel: WithCertType<CertificateModel>
Expand All @@ -209,7 +221,7 @@ export const mapCertificate = (
__typename: Cardano.CertificateType.PoolRegistration,
cert_index: certModel.cert_index,
deposit: BigInt(certModel.deposit),
poolParameters: null as unknown as Cardano.PoolParameters
poolParameters: mapPoolParameters(certModel)
} as WithCertIndex<Cardano.HydratedPoolRegistrationCertificate>;

if (isMirCertModel(certModel)) {
Expand All @@ -231,25 +243,15 @@ export const mapCertificate = (
: Cardano.CertificateType.Unregistration,
cert_index: certModel.cert_index,
deposit: BigInt(certModel.deposit),
stakeCredential: {
hash: Cardano.RewardAccount.toHash(
Cardano.RewardAccount(certModel.address)
) as unknown as Crypto.Hash28ByteBase16,
type: Cardano.CredentialType.KeyHash
}
stakeCredential: Cardano.Address.fromBech32(certModel.address).asReward()!.getPaymentCredential()
} as WithCertIndex<Cardano.NewStakeAddressCertificate>;

if (isDelegationCertModel(certModel))
return {
__typename: Cardano.CertificateType.StakeDelegation,
cert_index: certModel.cert_index,
poolId: certModel.pool_id as unknown as Cardano.PoolId,
stakeCredential: {
hash: Cardano.RewardAccount.toHash(
Cardano.RewardAccount(certModel.address)
) as unknown as Crypto.Hash28ByteBase16,
type: Cardano.CredentialType.KeyHash
}
stakeCredential: Cardano.Address.fromBech32(certModel.address).asReward()!.getPaymentCredential()
} as WithCertIndex<Cardano.StakeDelegationCertificate>;

if (isDrepRegistrationCertModel(certModel))
Expand Down Expand Up @@ -292,12 +294,7 @@ export const mapCertificate = (
__typename: Cardano.CertificateType.VoteDelegation,
cert_index: certModel.cert_index,
dRep: mapDrepDelegation(certModel),
stakeCredential: {
hash: Cardano.RewardAccount.toHash(
Cardano.RewardAccount(certModel.address)
) as unknown as Crypto.Hash28ByteBase16,
type: Cardano.CredentialType.KeyHash
}
stakeCredential: Cardano.Address.fromBech32(certModel.address).asReward()!.getPaymentCredential()
};

if (isVoteRegistrationDelegationCertModel(certModel))
Expand All @@ -306,12 +303,7 @@ export const mapCertificate = (
cert_index: certModel.cert_index,
dRep: mapDrepDelegation(certModel),
deposit: BigInt(certModel.deposit),
stakeCredential: {
hash: Cardano.RewardAccount.toHash(
Cardano.RewardAccount(certModel.address)
) as unknown as Crypto.Hash28ByteBase16,
type: Cardano.CredentialType.KeyHash
}
stakeCredential: Cardano.Address.fromBech32(certModel.address).asReward()!.getPaymentCredential()
};

if (isStakeVoteDelegationCertModel(certModel))
Expand All @@ -320,12 +312,7 @@ export const mapCertificate = (
cert_index: certModel.cert_index,
dRep: mapDrepDelegation(certModel),
poolId: certModel.pool_id as unknown as Cardano.PoolId,
stakeCredential: {
hash: Cardano.RewardAccount.toHash(
Cardano.RewardAccount(certModel.address)
) as unknown as Crypto.Hash28ByteBase16,
type: Cardano.CredentialType.KeyHash
}
stakeCredential: Cardano.Address.fromBech32(certModel.address).asReward()!.getPaymentCredential()
};

if (isStakeRegistrationDelegationCertModel(certModel))
Expand All @@ -334,12 +321,7 @@ export const mapCertificate = (
cert_index: certModel.cert_index,
deposit: BigInt(certModel.deposit),
poolId: certModel.pool_id as unknown as Cardano.PoolId,
stakeCredential: {
hash: Cardano.RewardAccount.toHash(
Cardano.RewardAccount(certModel.address)
) as unknown as Crypto.Hash28ByteBase16,
type: Cardano.CredentialType.KeyHash
}
stakeCredential: Cardano.Address.fromBech32(certModel.address).asReward()!.getPaymentCredential()
};

if (isStakeVoteRegistrationDelegationCertModel(certModel))
Expand All @@ -349,12 +331,7 @@ export const mapCertificate = (
dRep: mapDrepDelegation(certModel),
deposit: BigInt(certModel.deposit),
poolId: certModel.pool_id as unknown as Cardano.PoolId,
stakeCredential: {
hash: Cardano.RewardAccount.toHash(
Cardano.RewardAccount(certModel.address)
) as unknown as Crypto.Hash28ByteBase16,
type: Cardano.CredentialType.KeyHash
}
stakeCredential: Cardano.Address.fromBech32(certModel.address).asReward()!.getPaymentCredential()
};

if (isAuthorizeCommitteeHotCertModel(certModel))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,15 +250,21 @@ export const findPoolRetireCertsTxIds = `
export const findPoolRegisterCertsByTxIds = `
SELECT
cert.cert_index AS cert_index,
pool."view" AS pool_id,
pool.view AS pool_id,
tx.hash AS tx_id,
CASE
WHEN cert.deposit IS NULL THEN '0'
ELSE cert.deposit
END AS deposit
END AS deposit,
stake_address.view AS reward_account,
pledge,
fixed_cost,
margin,
vrf_key_hash
FROM tx
JOIN pool_update AS cert ON cert.registered_tx_id = tx.id
JOIN pool_hash AS pool ON pool.id = cert.hash_id
JOIN stake_address ON stake_address.id = reward_addr_id
WHERE tx.id = ANY($1)
ORDER BY tx.id ASC`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ export interface PoolRetireCertModel extends CertificateModel {
export interface PoolRegisterCertModel extends CertificateModel {
pool_id: string;
deposit: string;
reward_account: string;
pledge: string;
fixed_cost: string;
margin: number;
vrf_key_hash: Buffer;
}

export interface MirCertModel extends CertificateModel {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const blockHash = '7a48b034645f51743550bbaf81f8a14771e58856e031eb63844738ca8ad72
const poolId = 'pool1zuevzm3xlrhmwjw87ec38mzs02tlkwec9wxpgafcaykmwg7efhh';
const datetime = '2022-05-10T19:22:43.620Z';
const vrfKey = 'vrf_vk19j362pkr4t9y0m3qxgmrv0365vd7c4ze03ny4jh84q8agjy4ep4s99zvg8';
const vrfKeyHash = '220ba9398e3e5fae23a83d0d5927649d577a5f69d6ef1d5253c259d9393ba294';
const genesisLeaderHash = 'eff1b5b26e65b791d6f236c7c0264012bd1696759d22bdb4dd0f6f56';
const transactionHash = 'cefd2fcf657e5e5d6c35975f4e052f427819391b153ebb16ad8aa107ba5a3819';
const sourceTransactionHash = 'cefd2fcf657e5e5d6c35975f4e052f427819391b153ebb16ad8aa107ba5a3812';
Expand Down Expand Up @@ -292,14 +293,28 @@ describe('chain history mappers', () => {
const result = mappers.mapCertificate({
...baseCertModel,
deposit: '500000000',
fixed_cost: '390000000',
margin: 0.15,
pledge: '420000000',
pool_id: poolId,
type: 'register'
reward_account: stakeAddress,
type: 'register',
vrf_key_hash: Buffer.from(vrfKeyHash, 'hex')
} as WithCertType<PoolRegisterCertModel>);
expect(result).toEqual<WithCertIndex<Cardano.HydratedPoolRegistrationCertificate>>({
__typename: Cardano.CertificateType.PoolRegistration,
cert_index: 0,
deposit: 500_000_000n,
poolParameters: null as unknown as Cardano.PoolParameters
poolParameters: {
cost: 390_000_000n,
id: poolId as Cardano.PoolId,
margin: { denominator: 20, numerator: 3 },
owners: [],
pledge: 420_000_000n,
relays: [],
rewardAccount: stakeAddress as Cardano.RewardAccount,
vrf: vrfKeyHash as Cardano.VrfVkHex
}
});
});
test('map MirCertModel to Cardano.MirCertificate', () => {
Expand Down
Loading

0 comments on commit 7c2d694

Please sign in to comment.