Skip to content

Commit

Permalink
fix: save tx_index on locations to support transfers on same block (#145
Browse files Browse the repository at this point in the history
)

* fix: add tx_index to location tables

* fix: loc pointer conflict resolution

* test: multiple transfers same block
  • Loading branch information
rafaelcr committed Jul 12, 2023
1 parent eee9c41 commit 30a9635
Show file tree
Hide file tree
Showing 12 changed files with 452 additions and 205 deletions.
10 changes: 9 additions & 1 deletion migrations/1677284495299_locations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export function up(pgm: MigrationBuilder): void {
type: 'text',
notNull: true,
},
tx_index: {
type: 'bigint',
notNull: true,
},
address: {
type: 'text',
},
Expand Down Expand Up @@ -59,7 +63,11 @@ export function up(pgm: MigrationBuilder): void {
);
pgm.createConstraint('locations', 'locations_output_offset_unique', 'UNIQUE(output, "offset")');
pgm.createIndex('locations', ['inscription_id']);
pgm.createIndex('locations', ['genesis_id']);
pgm.createIndex('locations', [
'genesis_id',
{ name: 'block_height', sort: 'DESC' },
{ name: 'tx_index', sort: 'DESC' },
]);
pgm.createIndex('locations', ['block_height']);
pgm.createIndex('locations', ['block_hash']);
pgm.createIndex('locations', ['address']);
Expand Down
5 changes: 5 additions & 0 deletions migrations/1688925112931_genesis-locations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@ export function up(pgm: MigrationBuilder): void {
type: 'bigint',
notNull: true,
},
tx_index: {
type: 'bigint',
notNull: true,
},
});
pgm.createConstraint(
'genesis_locations',
'genesis_locations_inscription_id_unique',
'UNIQUE(inscription_id)'
);
pgm.createIndex('genesis_locations', ['location_id']);
pgm.createIndex('genesis_locations', ['block_height']);
}
5 changes: 5 additions & 0 deletions migrations/1689006001522_current-locations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@ export function up(pgm: MigrationBuilder): void {
type: 'bigint',
notNull: true,
},
tx_index: {
type: 'bigint',
notNull: true,
},
});
pgm.createConstraint(
'current_locations',
'current_locations_inscription_id_unique',
'UNIQUE(inscription_id)'
);
pgm.createIndex('current_locations', ['location_id']);
pgm.createIndex('current_locations', ['block_height']);
}
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

82 changes: 40 additions & 42 deletions src/pg/pg-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { isProdEnv, normalizedHexString, parseSatPoint } from '../api/util/helpe
import { OrdinalSatoshi, SatoshiRarity } from '../api/util/ordinal-satoshi';
import { ENV } from '../env';
import { logger } from '../logger';
import { getIndexResultCountType, inscriptionContentToJson } from './helpers';
import { getIndexResultCountType } from './helpers';
import { runMigrations } from './migrations';
import { connectPostgres } from './postgres-tools';
import { BasePgStore } from './postgres-tools/base-pg-store';
Expand All @@ -19,11 +19,10 @@ import {
DbInscriptionIndexResultCountType,
DbInscriptionInsert,
DbInscriptionLocationChange,
DbJsonContent,
DbLocation,
DbLocationInsert,
DbLocationPointerInsert,
DbPaginatedResult,
JSON_CONTENTS_COLUMNS,
LOCATIONS_COLUMNS,
} from './types';

Expand Down Expand Up @@ -117,6 +116,7 @@ export class PgStore extends BasePgStore {
block_hash,
block_height,
tx_id,
tx_index: reveal.tx_index,
genesis_id: reveal.inscription_id,
address: reveal.inscriber_address,
output: `${satpoint.tx_id}:${satpoint.vout}`,
Expand Down Expand Up @@ -150,6 +150,7 @@ export class PgStore extends BasePgStore {
block_hash,
block_height,
tx_id,
tx_index: reveal.tx_index,
genesis_id: reveal.inscription_id,
address: reveal.inscriber_address,
output: `${satpoint.tx_id}:${satpoint.vout}`,
Expand All @@ -170,6 +171,7 @@ export class PgStore extends BasePgStore {
block_hash,
block_height,
tx_id,
tx_index: transfer.tx_index,
genesis_id: transfer.inscription_id,
address: transfer.updated_address,
output: `${satpoint.tx_id}:${satpoint.vout}`,
Expand Down Expand Up @@ -448,16 +450,18 @@ export class PgStore extends BasePgStore {
args: InscriptionIdentifier & { limit: number; offset: number }
): Promise<DbPaginatedResult<DbLocation>> {
const results = await this.sql<({ total: number } & DbLocation)[]>`
SELECT ${this.sql(LOCATIONS_COLUMNS.map(c => `l.${c}`))}, COUNT(*) OVER() as total
FROM locations AS l
INNER JOIN inscriptions AS i ON l.inscription_id = i.id
WHERE
${
SELECT ${this.sql(LOCATIONS_COLUMNS)}, COUNT(*) OVER() as total
FROM locations
WHERE genesis_id = (
SELECT genesis_id FROM inscriptions
WHERE ${
'number' in args
? this.sql`i.number = ${args.number}`
: this.sql`i.genesis_id = ${args.genesis_id}`
? this.sql`number = ${args.number}`
: this.sql`genesis_id = ${args.genesis_id}`
}
ORDER BY l.block_height DESC
LIMIT 1
)
ORDER BY block_height DESC, tx_index DESC
LIMIT ${args.limit}
OFFSET ${args.offset}
`;
Expand Down Expand Up @@ -516,24 +520,6 @@ export class PgStore extends BasePgStore {
};
}

async getJsonContent(args: InscriptionIdentifier): Promise<DbJsonContent | undefined> {
const results = await this.sql<DbJsonContent[]>`
SELECT ${this.sql(JSON_CONTENTS_COLUMNS.map(c => `j.${c}`))}
FROM json_contents AS j
INNER JOIN inscriptions AS i ON j.inscription_id = i.id
WHERE
${
'number' in args
? this.sql`i.number = ${args.number}`
: this.sql`i.genesis_id = ${args.genesis_id}`
}
LIMIT 1
`;
if (results.count === 1) {
return results[0];
}
}

async getInscriptionCountPerBlock(
filters: DbInscriptionCountPerBlockFilters
): Promise<DbInscriptionCountPerBlock[]> {
Expand Down Expand Up @@ -595,6 +581,7 @@ export class PgStore extends BasePgStore {
block_height: args.location.block_height,
block_hash: args.location.block_hash,
tx_id: args.location.tx_id,
tx_index: args.location.tx_index,
address: args.location.address,
output: args.location.output,
offset: args.location.offset,
Expand All @@ -603,14 +590,15 @@ export class PgStore extends BasePgStore {
value: args.location.value,
timestamp: sql`to_timestamp(${args.location.timestamp})`,
};
const locationRes = await sql<{ id: string }[]>`
const locationRes = await sql<{ id: number }[]>`
INSERT INTO locations ${sql(location)}
ON CONFLICT ON CONSTRAINT locations_output_offset_unique DO UPDATE SET
inscription_id = EXCLUDED.inscription_id,
genesis_id = EXCLUDED.genesis_id,
block_height = EXCLUDED.block_height,
block_hash = EXCLUDED.block_hash,
tx_id = EXCLUDED.tx_id,
tx_index = EXCLUDED.tx_index,
address = EXCLUDED.address,
value = EXCLUDED.value,
timestamp = EXCLUDED.timestamp
Expand All @@ -621,6 +609,7 @@ export class PgStore extends BasePgStore {
genesis_id: args.inscription.genesis_id,
location_id: locationRes[0].id,
block_height: args.location.block_height,
tx_index: args.location.tx_index,
});
logger.info(
`PgStore${upsert.count > 0 ? ' upsert ' : ' '}reveal #${args.inscription.number} (${
Expand Down Expand Up @@ -667,6 +656,7 @@ export class PgStore extends BasePgStore {
block_height: args.location.block_height,
block_hash: args.location.block_hash,
tx_id: args.location.tx_id,
tx_index: args.location.tx_index,
address: args.location.address,
output: args.location.output,
offset: args.location.offset,
Expand All @@ -675,14 +665,15 @@ export class PgStore extends BasePgStore {
value: args.location.value,
timestamp: this.sql`to_timestamp(${args.location.timestamp})`,
};
const locationRes = await sql<{ id: string }[]>`
const locationRes = await sql<{ id: number }[]>`
INSERT INTO locations ${sql(location)}
ON CONFLICT ON CONSTRAINT locations_output_offset_unique DO UPDATE SET
inscription_id = EXCLUDED.inscription_id,
genesis_id = EXCLUDED.genesis_id,
block_height = EXCLUDED.block_height,
block_hash = EXCLUDED.block_hash,
tx_id = EXCLUDED.tx_id,
tx_index = EXCLUDED.tx_index,
address = EXCLUDED.address,
value = EXCLUDED.value,
timestamp = EXCLUDED.timestamp
Expand All @@ -694,6 +685,7 @@ export class PgStore extends BasePgStore {
genesis_id: args.location.genesis_id,
location_id: locationRes[0].id,
block_height: args.location.block_height,
tx_index: args.location.tx_index,
});
}
logger.info(
Expand Down Expand Up @@ -770,32 +762,38 @@ export class PgStore extends BasePgStore {
);
}

private async updateInscriptionLocationPointers(args: {
inscription_id: number;
genesis_id: string;
location_id: string;
block_height: number;
}): Promise<void> {
private async updateInscriptionLocationPointers(
args: DbLocationPointerInsert & { genesis_id: string }
): Promise<void> {
await this.sqlWriteTransaction(async sql => {
// Update genesis and current location pointers for this inscription.
const pointer = {
const pointer: DbLocationPointerInsert = {
inscription_id: args.inscription_id,
location_id: args.location_id,
block_height: args.block_height,
tx_index: args.tx_index,
};
await sql`
INSERT INTO genesis_locations ${sql(pointer)}
ON CONFLICT ON CONSTRAINT genesis_locations_inscription_id_unique DO UPDATE SET
location_id = EXCLUDED.location_id,
block_height = EXCLUDED.block_height
WHERE EXCLUDED.block_height < genesis_locations.block_height
block_height = EXCLUDED.block_height,
tx_index = EXCLUDED.tx_index
WHERE
EXCLUDED.block_height < genesis_locations.block_height OR
(EXCLUDED.block_height = genesis_locations.block_height AND
EXCLUDED.tx_index < genesis_locations.tx_index)
`;
await sql`
INSERT INTO current_locations ${sql(pointer)}
ON CONFLICT ON CONSTRAINT current_locations_inscription_id_unique DO UPDATE SET
location_id = EXCLUDED.location_id,
block_height = EXCLUDED.block_height
WHERE EXCLUDED.block_height > current_locations.block_height
block_height = EXCLUDED.block_height,
tx_index = EXCLUDED.tx_index
WHERE
EXCLUDED.block_height > current_locations.block_height OR
(EXCLUDED.block_height = current_locations.block_height AND
EXCLUDED.tx_index > current_locations.tx_index)
`;
// Backfill orphan locations for this inscription, if any.
await sql`
Expand Down
30 changes: 12 additions & 18 deletions src/pg/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Order, OrderBy } from '../api/schemas';
import { SatoshiRarity } from '../api/util/ordinal-satoshi';
import { OpJson } from './helpers';
import { PgBytea, PgJsonb, PgNumeric } from './postgres-tools/types';
import { PgBytea, PgNumeric } from './postgres-tools/types';

export type DbPaginatedResult<T> = {
total: number;
Expand All @@ -19,6 +18,7 @@ export type DbFullyLocatedInscriptionResult = {
number: string;
address: string | null;
tx_id: string;
tx_index: number;
output: string;
offset: string | null;
value: string | null;
Expand All @@ -37,6 +37,7 @@ export type DbLocationInsert = {
block_height: number;
block_hash: string;
tx_id: string;
tx_index: number;
address: string | null;
output: string;
offset: PgNumeric | null;
Expand All @@ -53,6 +54,7 @@ export type DbLocation = {
block_height: string;
block_hash: string;
tx_id: string;
tx_index: number;
address: string | null;
output: string;
offset: string | null;
Expand All @@ -62,6 +64,13 @@ export type DbLocation = {
timestamp: Date;
};

export type DbLocationPointerInsert = {
inscription_id: number;
location_id: number;
block_height: number;
tx_index: number;
};

export type DbInscriptionLocationChange = {
genesis_id: string;
number: string;
Expand Down Expand Up @@ -98,6 +107,7 @@ export const LOCATIONS_COLUMNS = [
'block_height',
'block_hash',
'tx_id',
'tx_index',
'address',
'output',
'offset',
Expand Down Expand Up @@ -152,22 +162,6 @@ export const INSCRIPTIONS_COLUMNS = [
'sat_coinbase_height',
];

export type DbJsonContent = {
id: string;
inscription_id: string;
p?: string;
op?: string;
content: OpJson;
};

export type DbJsonContentInsert = {
p: string | null;
op: string | null;
content: PgJsonb;
};

export const JSON_CONTENTS_COLUMNS = ['id', 'inscription_id', 'p', 'op', 'content'];

export type DbInscriptionIndexPaging = {
limit: number;
offset: number;
Expand Down
Loading

0 comments on commit 30a9635

Please sign in to comment.