Skip to content

Commit

Permalink
Hotfix
Browse files Browse the repository at this point in the history
  • Loading branch information
opcatdev committed Jan 14, 2025
1 parent d02dca8 commit aae3f4a
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 6 deletions.
4 changes: 4 additions & 0 deletions packages/tracker/src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ export class Constants {
static readonly TRANSFER_GUARD_MASK_OFFSET =
this.TRANSFER_GUARD_AMOUNT_OFFSET + this.CONTRACT_OUTPUT_MAX_COUNT;

static readonly TRANSFER_GUARD_INPUT_TOKEN_SCRIPT_OFFSET = 26;
static readonly TRANSFER_GUARD_INPUT_AMOUNT_OFFSET = 27;
static readonly TOKEN_INPUT_MAX_COUNT = 6;

static readonly QUERY_PAGING_DEFAULT_OFFSET = 0;
static readonly QUERY_PAGING_DEFAULT_LIMIT = 100;
static readonly QUERY_PAGING_MAX_LIMIT = 500;
Expand Down
7 changes: 7 additions & 0 deletions packages/tracker/src/common/exceptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@ export class CatTxError extends Error {
this.name = 'CatTxError';
}
}

export class TransferTxError extends Error {
constructor(message: string) {
super(message);
this.name = 'TransferTxError';
}
}
3 changes: 2 additions & 1 deletion packages/tracker/src/services/tx/tx.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { TxEntity } from '../../entities/tx.entity';
import { TokenInfoEntity } from '../../entities/tokenInfo.entity';
import { CommonModule } from '../common/common.module';
import { ScheduleModule } from '@nestjs/schedule';
import { TxOutEntity } from '../../entities/txOut.entity';

@Module({
imports: [
TypeOrmModule.forFeature([TxEntity, TokenInfoEntity]),
TypeOrmModule.forFeature([TxEntity, TokenInfoEntity, TxOutEntity]),
CommonModule,
ScheduleModule.forRoot(),
],
Expand Down
82 changes: 77 additions & 5 deletions packages/tracker/src/services/tx/tx.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Constants } from '../../common/constants';
import { TokenInfoEntity } from '../../entities/tokenInfo.entity';
import { NftInfoEntity } from '../../entities/nftInfo.entity';
import { CatTxError } from '../../common/exceptions';
import { CatTxError, TransferTxError } from '../../common/exceptions';
import { parseTokenInfoEnvelope } from '../../common/utils';
import {
BlockHeader,
Expand Down Expand Up @@ -52,6 +52,8 @@ export class TxService {
private tokenInfoEntityRepository: Repository<TokenInfoEntity>,
@InjectRepository(TxEntity)
private txEntityRepository: Repository<TxEntity>,
@InjectRepository(TxOutEntity)
private txOutEntityRepository: Repository<TxOutEntity>,
) {
this.dataSource = this.txEntityRepository.manager.connection;
}
Expand Down Expand Up @@ -141,12 +143,19 @@ export class TxService {
await queryRunner.commitTransaction();
return Math.ceil(Date.now() - startTs);
} catch (e) {
if (e instanceof CatTxError) {
this.logger.log(`skip tx ${tx.getId()}, ${e.message}`);
if (e instanceof TransferTxError) {
// do not rollback for TransferTxError
this.logger.error(
`[502750] invalid transfer tx ${tx.getId()}, ${e.message}`,
);
} else {
this.logger.error(`process tx ${tx.getId()} error, ${e.message}`);
if (e instanceof CatTxError) {
this.logger.log(`skip tx ${tx.getId()}, ${e.message}`);
} else {
this.logger.error(`process tx ${tx.getId()} error, ${e.message}`);
}
await queryRunner.rollbackTransaction();
}
await queryRunner.rollbackTransaction();
} finally {
await queryRunner.release();
}
Expand Down Expand Up @@ -659,6 +668,10 @@ export class TxService {
if (this.commonService.isTransferGuard(guardInput)) {
const tokenOutputs =
this.commonService.parseTransferTxTokenOutputs(guardInput);

// verify the token amount in guard witness equals to it in token input
await this.verifyGuardTokenAmount(guardInput, tx);

// save tx outputs
promises.push(
manager.save(
Expand All @@ -676,6 +689,65 @@ export class TxService {
return stateHashes;
}

public async verifyGuardTokenAmount(
guardInput: TaprootPayment,
tx: Transaction,
) {
const timeBefore = Date.now();
const tokenScript =
guardInput.witness[
Constants.TRANSFER_GUARD_INPUT_TOKEN_SCRIPT_OFFSET
].toString('hex');
const tokenAmounts = guardInput.witness.slice(
Constants.TRANSFER_GUARD_INPUT_AMOUNT_OFFSET,
Constants.TRANSFER_GUARD_INPUT_AMOUNT_OFFSET +
Constants.TOKEN_INPUT_MAX_COUNT,
);
await Promise.all(
tokenAmounts.map(async (amount, i) => {
const tokenAmount =
amount.length === 0 ? 0n : BigInt(amount.readIntLE(0, amount.length));
if (tokenAmount === 0n) {
return Promise.resolve();
}
const input = tx.ins[i];
const prevTxid = Buffer.from(input.hash).reverse().toString('hex');
const prevOutputIndex = input.index;
const prevout = `${prevTxid}:${prevOutputIndex}`;
return this.txOutEntityRepository
.findOne({
where: {
txid: prevTxid,
outputIndex: prevOutputIndex,
},
})
.then((prevOutput) => {
if (!prevOutput) {
this.logger.error(`prevout ${prevout} not found`);
throw new TransferTxError(
'invalid transfer tx, token input prevout is missing',
);
}
if (prevOutput.lockingScript !== tokenScript) {
this.logger.error(`prevout ${prevout} token script mismatches`);
throw new TransferTxError(
'invalid transfer tx, token script in guard not equal to it in token input',
);
}
if (BigInt(prevOutput.tokenAmount) !== tokenAmount) {
this.logger.error(
`prevout ${prevout} token amount mismatches, ${prevOutput.tokenAmount} !== ${tokenAmount}`,
);
throw new TransferTxError(
'invalid transfer tx, token amount in guard not equal to it in token input',
);
}
});
}),
);
this.logger.debug(`verifyGuardTokenAmount: ${Date.now() - timeBefore} ms`);
}

/**
* Parse state root hash from tx
*/
Expand Down

0 comments on commit aae3f4a

Please sign in to comment.