diff --git a/src/constants.ts b/src/constants.ts index a1b05e6b..d80c4590 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -28,3 +28,16 @@ export const JWT_IGNORE_URLS = ['/token', '/docs', '/cron', '/internal', '/healt export const SWAGGER_PROD_IGNORE_URLS = isAdminMode ? ['/token', '/cron'] : ['/cron']; export const VERCEL_MAX_DURATION = 300; + +// estimate time: 2024-04-03 09:45:17 +// ref: https://mempool.space/testnet/block/000000000000000493ba5eebf0602f3e0e5381dd35f763a62ca7ea135343a0d6 +export const BTC_TESTNET_SPV_START_BLOCK_HEIGHT = 2584900; + +// estimate time: 2024-06-13 18:31:56 +// ref: https://mempool.space/signet/block/000000b2af39a66ec81d414b102804d975c5c4527adfd9bd3cabf2b7b4634737 +// Signet BTC SPV deployment time: https://pudge.explorer.nervos.org/transaction/0x61efdeddbaa0bb4132c0eb174b3e8002ff5ec430f61ba46f30768d683c516eec +export const BTC_SIGNET_SPV_START_BLOCK_HEIGHT = 199800; + +// estimate time: 2024-04-02 06:20:03 +// ref: https://mempool.space/block/0000000000000000000077d98a103858c7d7cbc5ba67a4135f348a436bec1748 +export const BTC_MAINNET_SPV_START_BLOCK_HEIGHT = 837300; diff --git a/src/services/unlocker.ts b/src/services/unlocker.ts index 623948c2..f8ffbc2b 100644 --- a/src/services/unlocker.ts +++ b/src/services/unlocker.ts @@ -17,7 +17,12 @@ import { import { btcTxIdFromBtcTimeLockArgs } from '@rgbpp-sdk/ckb/lib/utils/rgbpp'; import { BtcAssetsApi } from '@rgbpp-sdk/service'; import { Cradle } from '../container'; -import { TestnetTypeMap } from '../constants'; +import { + BTC_MAINNET_SPV_START_BLOCK_HEIGHT, + BTC_SIGNET_SPV_START_BLOCK_HEIGHT, + BTC_TESTNET_SPV_START_BLOCK_HEIGHT, + TestnetTypeMap, +} from '../constants'; interface IUnlocker { getNextBatchLockCell(): Promise; @@ -54,6 +59,17 @@ export default class Unlocker implements IUnlocker { return getBtcTimeLockScript(this.isMainnet, this.testnetType); } + private get btcSpvStartBlockHeight() { + const network = this.cradle.env.NETWORK; + if (network === 'mainnet') { + return BTC_MAINNET_SPV_START_BLOCK_HEIGHT; + } + if (network === 'testnet') { + return BTC_TESTNET_SPV_START_BLOCK_HEIGHT; + } + return BTC_SIGNET_SPV_START_BLOCK_HEIGHT; + } + /** * Get next batch of BTC time lock cells */ @@ -73,6 +89,11 @@ export default class Unlocker implements IUnlocker { const btcTx = await this.cradle.bitcoin.getTx({ txid: btcTxid }); const blockHeight = btcTx.status.block_height; + // skip if the block height of the btc txid is less than the BTC SPV start block height + if (blockHeight && blockHeight < this.btcSpvStartBlockHeight) { + continue; + } + // skip if btc tx not confirmed $after blocks yet if (!blockHeight || blocks - blockHeight < after) { continue; diff --git a/test/services/unlocker.test.ts b/test/services/unlocker.test.ts index 7512ca29..74a84ad9 100644 --- a/test/services/unlocker.test.ts +++ b/test/services/unlocker.test.ts @@ -75,9 +75,9 @@ describe('Unlocker', () => { test('getNextBatchLockCell: should skip unconfirmed btc tx', async () => { // @ts-expect-error - vi.spyOn(unlocker['cradle'].bitcoin, 'getBlockchainInfo').mockResolvedValue({ blocks: 100 }); + vi.spyOn(unlocker['cradle'].bitcoin, 'getBlockchainInfo').mockResolvedValue({ blocks: 2685000 }); // @ts-expect-error - vi.spyOn(unlocker['cradle'].bitcoin, 'getTx').mockResolvedValue({ status: { block_height: 95 } }); + vi.spyOn(unlocker['cradle'].bitcoin, 'getTx').mockResolvedValue({ status: { block_height: 2684995 } }); mockBtcTimeLockCell(); const cells = await unlocker.getNextBatchLockCell(); @@ -86,9 +86,9 @@ describe('Unlocker', () => { test('getNextBatchLockCell: should return cells when btc tx is confirmed', async () => { // @ts-expect-error - vi.spyOn(unlocker['cradle'].bitcoin, 'getBlockchainInfo').mockResolvedValue({ blocks: 101 }); + vi.spyOn(unlocker['cradle'].bitcoin, 'getBlockchainInfo').mockResolvedValue({ blocks: 2685001 }); // @ts-expect-error - vi.spyOn(unlocker['cradle'].bitcoin, 'getTx').mockResolvedValue({ status: { block_height: 95 } }); + vi.spyOn(unlocker['cradle'].bitcoin, 'getTx').mockResolvedValue({ status: { block_height: 2684995 } }); mockBtcTimeLockCell(); const cells = await unlocker.getNextBatchLockCell(); @@ -99,9 +99,9 @@ describe('Unlocker', () => { unlocker['cradle'].env.UNLOCKER_CELL_BATCH_SIZE = 1; // @ts-expect-error - vi.spyOn(unlocker['cradle'].bitcoin, 'getBlockchainInfo').mockResolvedValue({ blocks: 101 }); + vi.spyOn(unlocker['cradle'].bitcoin, 'getBlockchainInfo').mockResolvedValue({ blocks: 2685001 }); // @ts-expect-error - vi.spyOn(unlocker['cradle'].bitcoin, 'getTx').mockResolvedValue({ status: { block_height: 95 } }); + vi.spyOn(unlocker['cradle'].bitcoin, 'getTx').mockResolvedValue({ status: { block_height: 2684995 } }); mockBtcTimeLockCell(); const cells = await unlocker.getNextBatchLockCell();