11import BlockDbRepository from '@/kadena-server/repository/infra/repository/block-db-repository' ;
22import { increaseCounters } from '@/services/counters' ;
33import { markCanonicalTip } from '@/utils/canonical-tip' ;
4+ import { getRequiredEnvString } from '@/utils/helpers' ;
5+ import { processPayload , saveBlock } from './streaming' ;
6+ import { sequelize } from '@/config/database' ;
7+ import { Transaction } from 'sequelize' ;
48
59const blockRepository = new BlockDbRepository ( ) ;
10+ const SYNC_BASE_URL = getRequiredEnvString ( 'SYNC_BASE_URL' ) ;
11+ const SYNC_NETWORK = getRequiredEnvString ( 'SYNC_NETWORK' ) ;
12+
13+ export async function defineCanonicalBaseline (
14+ blockHash : string ,
15+ parentHash : string ,
16+ height : number ,
17+ chainId : number ,
18+ ) {
19+ try {
20+ const parentBlock = await blockRepository . getBlockByHash ( parentHash ) ;
21+ if ( ! parentBlock ) {
22+ console . log ( `[INFO][SYNC][DEFINE_CANONICAL] parentBlock not found. filling gaps ...` ) ;
23+ console . log ( `[chainId: ${ chainId } , parentHash: ${ parentHash } , height: ${ height - 1 } ]` ) ;
24+ await fillChainGapAndConfirmBlockPath ( parentHash , height - 1 , chainId ) ;
25+ }
26+ } catch ( error ) {
27+ console . error ( `[ERROR][SYNC][DEFINE_CANONICAL] Error filling gaps:` , error ) ;
28+ return ;
29+ }
630
7- export async function defineCanonicalInStreaming ( blockHash : string ) {
831 const tipBlock = await blockRepository . getBlockByHash ( blockHash ) ;
932 if ( ! tipBlock ) {
10- console . error ( '[ERROR][DATA][DATA_CORRUPT] Error defining canonical in streaming:' , blockHash ) ;
11- return ;
33+ // this scenario should not happen, but if it does, terminate the app.
34+ console . error ( `[ERROR][SYNC][DEFINE_CANONICAL] Block ${ blockHash } not found in database` ) ;
35+ process . exit ( 1 ) ;
36+ }
37+
38+ let tx : Transaction ;
39+ try {
40+ tx = await sequelize . transaction ( ) ;
41+ } catch ( error ) {
42+ console . error ( `[ERROR][SYNC][DEFINE_CANONICAL] Failed to start transaction:` , error ) ;
43+ throw error ;
1244 }
1345
1446 try {
1547 const blocksWithSameHeightOfTipBlock = await blockRepository . getBlocksWithSameHeight (
1648 tipBlock . height ,
1749 tipBlock . chainId ,
50+ tx ,
1851 ) ;
1952 const blocksWithHigherHeightOfTipBlock = await blockRepository . getBlocksWithHeightHigherThan (
2053 tipBlock . height ,
2154 tipBlock . chainId ,
55+ tx ,
2256 ) ;
2357 const {
2458 blocksBecameCanonical,
@@ -30,6 +64,7 @@ export async function defineCanonicalInStreaming(blockHash: string) {
3064 blocksWithSameHeightOfTipBlock,
3165 blocksWithHigherHeightOfTipBlock,
3266 tipBlock,
67+ tx,
3368 } ) ;
3469
3570 await increaseCounters ( {
@@ -38,8 +73,95 @@ export async function defineCanonicalInStreaming(blockHash: string) {
3873 canonicalTransactionsCount : transactionsBecameCanonical - transactionsBecameNonCanonical ,
3974 orphanTransactionsCount : transactionsBecameNonCanonical - transactionsBecameCanonical ,
4075 chainId : tipBlock . chainId ,
76+ tx,
4177 } ) ;
78+ await tx . commit ( ) ;
79+ } catch ( error ) {
80+ await tx . rollback ( ) ;
81+ console . error ( `[ERROR][SYNC][DEFINE_CANONICAL] Error defining canonical:` , error ) ;
82+ }
83+ }
84+
85+ async function fillChainGapAndConfirmBlockPath ( blockHash : string , height : number , chainId : number ) {
86+ let blocksByHash : Record < string , any > ;
87+ try {
88+ blocksByHash = await fetchBlocksFromChainwebNode ( chainId , height ) ;
4289 } catch ( error ) {
43- console . error ( 'Error defining canonical:' , error ) ;
90+ console . error ( `[ERROR][SYNC][FILL_GAPS] Failed to get blocks to fill gaps:` , error ) ;
91+ throw error ;
4492 }
93+
94+ let tx : Transaction ;
95+ try {
96+ tx = await sequelize . transaction ( ) ;
97+ } catch ( error ) {
98+ console . error ( `[ERROR][SYNC][FILL_GAPS] Failed to start transaction to fill gaps:` , error ) ;
99+ throw error ;
100+ }
101+
102+ let currentHash = blockHash ;
103+ try {
104+ while ( true ) {
105+ const existingBlock = await blockRepository . getBlockByHash ( currentHash ) ;
106+ if ( existingBlock ) {
107+ console . info (
108+ `[INFO][SYNC][FILL_GAPS] Found existing block: ${ currentHash } , stopping gap fill` ,
109+ ) ;
110+ break ;
111+ }
112+
113+ const currentBlockAPIData = blocksByHash [ currentHash ] ;
114+ if ( ! currentBlockAPIData ) {
115+ console . info ( `[INFO][SYNC][FILL_GAPS] API data all filled, stopping gap fill` ) ;
116+ break ;
117+ }
118+
119+ const payload = processPayload ( currentBlockAPIData . payloadWithOutputs ) ;
120+ await saveBlock ( { header : currentBlockAPIData . header , payload, canonical : true } , tx ) ;
121+
122+ // Move to the parent block
123+ currentHash = currentBlockAPIData . header . parent ;
124+ }
125+ await tx . commit ( ) ;
126+ } catch ( err ) {
127+ console . error ( `[ERROR][SYNC][FILL_GAPS] Failed to save block ${ currentHash } in:` , err ) ;
128+ await tx . rollback ( ) ;
129+ throw err ;
130+ }
131+ }
132+
133+ async function fetchBlocksFromChainwebNode (
134+ chainId : number ,
135+ height : number ,
136+ ) : Promise < Record < string , any > > {
137+ const cut = await fetch ( `${ SYNC_BASE_URL } /${ SYNC_NETWORK } /cut` , {
138+ method : 'GET' ,
139+ headers : {
140+ 'Content-Type' : 'application/json' ,
141+ } ,
142+ } ) ;
143+ const cutData = await cut . json ( ) ;
144+
145+ const upperHash = cutData . hashes [ chainId ] . hash ;
146+ const MIN_HEIGHT = height - 10 ; // 10 blocks is the max gap we can fill
147+ const url = `${ SYNC_BASE_URL } /${ SYNC_NETWORK } /chain/${ chainId } /block/branch?minheight=${ MIN_HEIGHT } &maxheight=${ height } ` ;
148+ const res = await fetch ( url , {
149+ method : 'POST' ,
150+ headers : {
151+ 'Content-Type' : 'application/json' ,
152+ } ,
153+ body : JSON . stringify ( {
154+ upper : [ upperHash ] ,
155+ } ) ,
156+ } ) ;
157+
158+ const data = await res . json ( ) ;
159+
160+ // Create a map of blocks by hash for easy lookup
161+ const blocksByHash = data . items . reduce ( ( acc : Record < string , any > , item : any ) => {
162+ acc [ item . header . hash ] = item ;
163+ return acc ;
164+ } , { } ) ;
165+
166+ return blocksByHash ;
45167}
0 commit comments