Skip to content

Commit a7ad2d5

Browse files
committed
feat: process fork in indexer mode
1 parent a038e18 commit a7ad2d5

7 files changed

Lines changed: 118 additions & 11 deletions

File tree

packages/neuron-wallet/src/database/chain/entities/transaction.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ export default class Transaction extends BaseEntity {
8383
})
8484
updatedAt!: string
8585

86+
// only used for check fork in indexer mode
87+
@Column({
88+
type: 'boolean',
89+
})
90+
confirmed: boolean = false
91+
8692
@OneToMany(_type => InputEntity, input => input.transaction)
8793
inputs!: InputEntity[]
8894

packages/neuron-wallet/src/database/chain/migrations/1562038960990-AddStatusToTx.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,33 @@
1-
import {MigrationInterface, QueryRunner, TableColumn, getConnection} from "typeorm";
1+
import {MigrationInterface, QueryRunner, TableColumn, getConnection, In} from "typeorm";
22
import TransactionEntity from '../entities/transaction'
33
import { OutputStatus } from '../../../services/tx/params'
44
import { TransactionStatus } from '../../../types/cell-types'
5+
import OutputEntity from 'database/chain/entities/output'
56

67
export class AddStatusToTx1562038960990 implements MigrationInterface {
78

89
public async up(queryRunner: QueryRunner): Promise<any> {
9-
await queryRunner.query(`ALTER TABLE 'transaction' ADD COLUMN 'status' varchar NOT NULL DEFAULT '';`)
10+
// TransactionStatus.Success = 'success'
11+
await queryRunner.query(`ALTER TABLE 'transaction' ADD COLUMN 'status' varchar NOT NULL DEFAULT 'success';`)
12+
13+
const pendingTxHashes: string[] = (await getConnection()
14+
.getRepository(OutputEntity)
15+
.createQueryBuilder('output')
16+
.select(`output.outPointTxHash`, 'txHash')
17+
.where({
18+
status: OutputStatus.Sent
19+
})
20+
.getRawMany())
21+
.filter(output => output.txHash)
22+
await getConnection()
23+
.createQueryBuilder()
24+
.update(TransactionEntity)
25+
.set({ status: TransactionStatus.Pending })
26+
.where({
27+
hash: In(pendingTxHashes)
28+
})
29+
.execute()
1030

11-
const txs = await getConnection()
12-
.getRepository(TransactionEntity)
13-
.find({ relations: ['inputs', 'outputs'] })
14-
const updatedTxs = txs.map(tx => {
15-
tx.status = tx.outputs[0].status === OutputStatus.Sent ? TransactionStatus.Pending : TransactionStatus.Success
16-
return tx
17-
})
18-
await getConnection().manager.save(updatedTxs)
1931
await queryRunner.changeColumn('transaction', 'status', new TableColumn({
2032
name: 'status',
2133
type: 'varchar',
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import {MigrationInterface, QueryRunner} from "typeorm";
2+
3+
export class AddConfirmed1565693320664 implements MigrationInterface {
4+
5+
public async up(queryRunner: QueryRunner): Promise<any> {
6+
await queryRunner.query(`ALTER TABLE 'transaction' ADD COLUMN 'confirmed' boolean NOT NULL DEFAULT false;`)
7+
}
8+
9+
public async down(queryRunner: QueryRunner): Promise<any> {
10+
await queryRunner.dropColumn('transaction', 'confirmed')
11+
}
12+
13+
}

packages/neuron-wallet/src/database/chain/ormconfig.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import SyncInfo from './entities/sync-info'
1212

1313
import { InitMigration1561695143591 } from './migrations/1561695143591-InitMigration'
1414
import { AddStatusToTx1562038960990 } from './migrations/1562038960990-AddStatusToTx'
15+
import { AddConfirmed1565693320664 } from './migrations/1565693320664-AddConfirmed'
1516

1617
export const CONNECTION_NOT_FOUND_NAME = 'ConnectionNotFoundError'
1718

@@ -31,7 +32,7 @@ const connectOptions = async (genesisBlockHash: string): Promise<SqliteConnectio
3132
type: 'sqlite',
3233
database: dbPath(genesisBlockHash),
3334
entities: [Transaction, Input, Output, SyncInfo],
34-
migrations: [InitMigration1561695143591, AddStatusToTx1562038960990],
35+
migrations: [InitMigration1561695143591, AddStatusToTx1562038960990, AddConfirmed1565693320664],
3536
logging,
3637
}
3738
}

packages/neuron-wallet/src/services/indexer/queue.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import BlockNumber from 'services/sync/block-number'
88
import AddressesUsedSubject from 'models/subjects/addresses-used-subject'
99
import LockUtils from 'models/lock-utils'
1010
import TransactionPersistor from 'services/tx/transaction-persistor'
11+
import IndexerTransaction from 'services/tx/indexer-transaction'
1112

1213
import IndexerRPC from './indexer-rpc'
1314

@@ -82,6 +83,27 @@ export default class Queue {
8283
}
8384
}
8485

86+
public processFork = async () => {
87+
while (!this.stopped) {
88+
try {
89+
const tip = this.tipBlockNumber
90+
const txs = await IndexerTransaction.txHashes()
91+
for (const tx of txs) {
92+
const result = await this.getBlocksService.getTransaction(tx.hash)
93+
if (!result) {
94+
await IndexerTransaction.deleteTxWhenFork(tx.hash)
95+
} else if (tip - BigInt(tx.blockNumber) >= 1000) {
96+
await IndexerTransaction.confirm(tx.hash)
97+
}
98+
}
99+
} catch (err) {
100+
logger.error(`indexer delete forked tx:`, err)
101+
} finally {
102+
await this.yield(10000)
103+
}
104+
}
105+
}
106+
85107
public getCurrentBlockNumber = async (lockHashes: string[]) => {
86108
// get lock hash indexer status
87109
const lockHashIndexStates = await this.indexerRPC.getLockHashIndexStates()
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { getConnection } from 'typeorm'
2+
import TransactionEntity from 'database/chain/entities/transaction'
3+
import Utils from 'services/sync/utils'
4+
import InputEntity from 'database/chain/entities/input'
5+
import OutputEntity from 'database/chain/entities/output'
6+
import { OutputStatus } from './params'
7+
8+
export default class IndexerTransaction {
9+
public static txHashes = async () => {
10+
const txs = await getConnection()
11+
.getRepository(TransactionEntity)
12+
.createQueryBuilder('tx')
13+
.where(`tx.confirmed = false`)
14+
.getMany()
15+
16+
return txs
17+
}
18+
19+
public static confirm = async (hash: string) => {
20+
await getConnection().manager.update(TransactionEntity, hash, { confirmed: true })
21+
}
22+
23+
public static deleteTxWhenFork = async (hash: string) => {
24+
const tx = await getConnection()
25+
.getRepository(TransactionEntity)
26+
.findOne(hash, { relations: ['inputs', 'outputs'] })
27+
28+
if (!tx) {
29+
return
30+
}
31+
32+
// reset previous output to OutputStatus.Live
33+
await getConnection().transaction(async transactionalEntityManager => {
34+
await Utils.mapSeries(tx.inputs, async (input: InputEntity) => {
35+
if (!input.lockHash) {
36+
return
37+
}
38+
39+
await transactionalEntityManager.update(
40+
OutputEntity,
41+
{
42+
outPointTxHash: input.outPointTxHash,
43+
outPointIndex: input.outPointIndex,
44+
},
45+
{ status: OutputStatus.Live }
46+
)
47+
})
48+
49+
await transactionalEntityManager.remove([tx, ...tx.inputs, ...tx.outputs])
50+
})
51+
}
52+
}

packages/neuron-wallet/src/startup/sync-block-task/indexer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,5 @@ export const switchNetwork = async (nodeURL: string) => {
4141
})
4242

4343
indexerQueue.start()
44+
indexerQueue.processFork()
4445
}

0 commit comments

Comments
 (0)