Skip to content

Commit 61d4291

Browse files
committed
feat: add script to get in chain tx cbor from tx id
1 parent 39ada85 commit 61d4291

File tree

3 files changed

+119
-1
lines changed

3 files changed

+119
-1
lines changed

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,18 @@ for you.
137137
nix run .#config-update
138138
```
139139

140+
## Get CBOR representation of an on chain transaction
141+
142+
Once we have a [running network](packages/cardano-services/README.md#production) synced at least up to the block
143+
containing the transaction we are interested in, issue following command to get the CBOR representation of the
144+
transaction.
145+
146+
```
147+
yarn tx-cbor <txId>
148+
```
149+
150+
This works regardless of the local ports configuration through environment variables.
151+
140152
## Attic
141153

142154
Previously supported features, no longer supported, but packed with a reference branch.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
"test": "yarn workspaces foreach -v run test",
3838
"test:build:verify": "yarn workspaces foreach -v run test:build:verify",
3939
"test:e2e": "yarn workspaces foreach -v run test:e2e",
40-
"test:debug": "DEBUG=true yarn workspaces foreach -v run test"
40+
"test:debug": "DEBUG=true yarn workspaces foreach -v run test",
41+
"tx-cbor": "tsx packages/cardano-services/scripts/tx-cbor.js"
4142
},
4243
"repository": {
4344
"type": "git",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { Pool } from 'pg';
2+
import { readFileSync } from 'fs';
3+
import { spawnSync } from 'child_process';
4+
import WebSocket from 'ws';
5+
6+
interface Tx {
7+
cbor: string;
8+
id: string;
9+
}
10+
11+
const txId = process.argv[2];
12+
13+
const normalizeError = (status: number | null, stderr: string) =>
14+
[status || 1, stderr || 'Unknown error\n', ''] as const;
15+
16+
const inside = async () => {
17+
const db = new Pool({
18+
database: readFileSync(process.env.POSTGRES_DB_FILE_DB_SYNC!).toString(),
19+
host: 'postgres',
20+
password: readFileSync(process.env.POSTGRES_PASSWORD_FILE_DB_SYNC!).toString(),
21+
user: readFileSync(process.env.POSTGRES_USER_FILE_DB_SYNC!).toString()
22+
});
23+
24+
const query = `\
25+
SELECT ENCODE(b2.hash, 'hex') AS id, b2.slot_no::INTEGER AS slot FROM tx
26+
JOIN block b1 ON block_id = b1.id
27+
JOIN block b2 ON b1.previous_id = b2.id
28+
WHERE tx.hash = $1`;
29+
30+
const { rows } = await db.query(query, [Buffer.from(txId, 'hex')]);
31+
const [prevBlock] = rows;
32+
33+
await db.end();
34+
35+
if (!prevBlock) return [1, `Unknown transaction id ${txId}\n`, ''] as const;
36+
37+
const cbor = await new Promise<string>((resolve) => {
38+
const client = new WebSocket(process.env.OGMIOS_URL!);
39+
let request = 0;
40+
41+
const rpc = (method: string, params: unknown) =>
42+
client.send(JSON.stringify({ id: ++request, jsonrpc: '2.0', method, params }));
43+
44+
client.on('open', () => rpc('findIntersection', { points: [prevBlock] }));
45+
46+
client.on('message', (msg) => {
47+
const { result } = JSON.parse(msg.toString()) as { result: { block: { transactions: Tx[] } } };
48+
let tx: Tx | undefined;
49+
50+
if (
51+
result &&
52+
result.block &&
53+
result.block.transactions &&
54+
(tx = result.block.transactions.find((t) => t.id === txId))
55+
) {
56+
client.on('close', () => resolve(tx!.cbor));
57+
client.close();
58+
} else rpc('nextBlock', {});
59+
});
60+
});
61+
62+
return [0, '', `${cbor}\n`] as const;
63+
};
64+
65+
const outside = async () => {
66+
if (!txId) return [1, 'Missing input transaction id\n', ''] as const;
67+
68+
let { status, stderr, stdout } = spawnSync('docker', ['ps'], { encoding: 'utf-8' });
69+
70+
if (status || stderr) return normalizeError(status, stderr);
71+
72+
const container = [
73+
'cardano-services-mainnet-provider-server-1',
74+
'cardano-services-preprod-provider-server-1',
75+
'cardano-services-preview-provider-server-1',
76+
'cardano-services-sanchonet-provider-server-1',
77+
'local-network-e2e-provider-server-1'
78+
].find((name) => stdout.includes(name));
79+
80+
if (!container) return [1, "Can't find any valid container\n", ''] as const;
81+
82+
({ status, stderr, stdout } = spawnSync(
83+
'docker',
84+
['container', 'exec', '-i', container, 'bash', '-c', `cd /app ; INSIDE_THE_CONTAINER=true yarn tx-cbor ${txId}`],
85+
{ encoding: 'utf-8' }
86+
));
87+
88+
if (status || stderr) return normalizeError(status, stderr);
89+
90+
return [0, '', stdout] as const;
91+
};
92+
93+
(process.env.INSIDE_THE_CONTAINER ? inside() : outside())
94+
.then(([status, stderr, stdout]) => {
95+
if (status) {
96+
process.stderr.write(stderr);
97+
// eslint-disable-next-line unicorn/no-process-exit
98+
process.exit(status);
99+
}
100+
101+
process.stdout.write(stdout);
102+
})
103+
.catch((error) => {
104+
throw error;
105+
});

0 commit comments

Comments
 (0)