2019-07-10 04:01:15 +00:00
/ *
* @project : TERA
* @version : Development ( beta )
* @license : MIT ( not for evil )
* @copyright : Yuriy Ivanov ( Vtools ) 2017 - 2019 [ progr76 @gmail . com ]
* Web : https : //terafoundation.org
* Twitter : https : //twitter.com/terafoundation
* Telegram : https : //t.me/terafoundation
* /
"use strict" ;
2019-07-11 05:35:16 +00:00
import CDB from './db/block-db'
2019-07-12 14:46:26 +00:00
import { TYPE_TRANSACTION } from '../constant/account' ;
2019-07-13 10:48:51 +00:00
import { DB_FORMAT } from '../constant/db-format' ;
2019-07-11 05:35:16 +00:00
export default class CRest extends CDB {
2019-07-10 07:59:04 +00:00
LoadRestContext
BlockNumDB
BlockNumDBMin
UseTruncateBlockDB
ContextSendLoadToBegin
2019-07-11 05:35:16 +00:00
LoadHistoryContext
2019-07-10 04:01:15 +00:00
constructor ( SetKeyPair , RunIP , RunPort , UseRNDHeader , bVirtual ) {
super ( SetKeyPair , RunIP , RunPort , UseRNDHeader , bVirtual )
}
CheckSyncRest() {
2019-07-10 07:59:04 +00:00
var BlockNumTime = global . GetCurrentBlockNumByTime ( ) ;
2019-07-10 04:01:15 +00:00
var Delta = BlockNumTime - this . BlockNumDB ;
2019-07-10 07:59:04 +00:00
if ( Delta > global . REST_START_COUNT + global . DELTA_BLOCK_ACCOUNT_HASH + 500 ) {
var BlockNumRest = GetCurrentRestNum ( global . REST_START_COUNT + global . DELTA_BLOCK_ACCOUNT_HASH + 500 ) ;
2019-07-10 04:01:15 +00:00
if ( this . BlockNumDB >= this . BlockNumDBMin && this . BlockNumDB <= this . BlockNumDBMin + global . BLOCK_PROCESSING_LENGTH2 ) {
}
else
if ( BlockNumRest > this . BlockNumDB ) {
}
else {
this . LoadRestContext = undefined
return ;
}
this . LoadRestContext = {
2019-07-10 07:59:04 +00:00
Mode : 0 , BlockNum : BlockNumRest , BlockNumRest : BlockNumRest , WasDelta : Delta , BlockNumProof : BlockNumRest + global . DELTA_BLOCK_ACCOUNT_HASH ,
CountProof : global.COUNT_BLOCKS_FOR_LOAD , StartTimeHistory : Date.now ( ) , MaxTimeOut : 600 * 1000 , LoopSyncRest : 1 , SendGetHeaderCount : 0 ,
2019-07-10 04:01:15 +00:00
ReceiveHeaderCount : 0 , ArrProof : [ ] , MapSend : { }
}
for ( var i = 0 ; i < this . NodesArr . length ; i ++ ) {
this . NodesArr [ i ] . SendRestGetHeader = 0
}
global . ToLog ( "**********START REST MODE: " + this . LoadRestContext . BlockNumProof )
}
else {
this . LoadRestContext = undefined
}
}
2019-07-11 05:35:16 +00:00
GetNextNode ( task , keyid , checktime ? , BlockNum ? ) : any {
//defiend in block-loader.ts(CBlock)
}
DataFromF ( Info , bSendFormat ? ) : any {
//defiend in block-loader.ts(CBlock)
}
AddToBan ( Node , Str ) {
//defiend in server.ts(CTransport)
}
2019-07-10 04:01:15 +00:00
LoopSyncRest() {
let Context = this . LoadRestContext ;
switch ( Context . Mode ) {
case 0 :
var ArrNodes = this . GetActualNodes ( ) ;
for ( var i = 0 ; i < ArrNodes . length ; i ++ ) {
var Node = ArrNodes [ i ] ;
if ( ! Node || Node . SendRestGetHeader ) {
continue ;
}
Node . SendRestGetHeader = 1
2019-07-11 05:35:16 +00:00
global . ToLog ( "Send rest get headers from " + Context . BlockNumProof + " to " + global . NodeName ( Node ) , 2 )
2019-07-10 04:01:15 +00:00
this . SendF ( Node , {
"Method" : "GETBLOCKHEADER" , "Data" : { Foward : 1 , BlockNum : Context.BlockNumProof , Hash : [ ] } , "Context" : { F : this.RETBLOCKHEADER_REST.bind ( this ) } ,
} )
Context . SendGetHeaderCount ++
break ;
}
2019-07-10 07:59:04 +00:00
if ( Context . ReceiveHeaderCount >= global . COUNT_NODE_PROOF ) {
2019-07-10 04:01:15 +00:00
Context . Mode = 2
global . ToLog ( "Next mode: " + Context . Mode + " Receive:" + Context . ReceiveHeaderCount + "/" + Context . SendGetHeaderCount , 2 )
}
break ;
case 1000 :
break ;
case 2 :
var MapSumPower = { } ;
for ( var i = 0 ; i < Context . ArrProof . length ; i ++ ) {
var Item = Context . ArrProof [ i ] ;
if ( ! MapSumPower [ Item . SumPower ] )
MapSumPower [ Item . SumPower ] = 0
MapSumPower [ Item . SumPower ] ++
}
var MaxCount = 0 , MaxPow = 0 ;
for ( var key in MapSumPower ) {
if ( MapSumPower [ key ] >= MaxCount ) {
MaxCount = MapSumPower [ key ]
MaxPow = parseInt ( key )
}
}
if ( MaxCount < 2 || MaxPow === 0 ) {
2019-07-13 07:37:38 +00:00
global . ToLog ( "****************************************************************** Error MaxPow=" + MaxPow + " - reload." )
2019-07-10 04:01:15 +00:00
this . CheckSyncRest ( )
return ;
}
for ( var i = 0 ; i < Context . ArrProof . length ; i ++ ) {
var Item = Context . ArrProof [ i ] ;
if ( Item . SumPower !== MaxPow ) {
var Str = "BAD SumPower: " + Item . SumPower + "/" + MaxPow ;
2019-07-11 05:35:16 +00:00
global . ToLog ( Str + " from: " + global . NodeName ( Item . Node ) , 2 )
2019-07-10 04:01:15 +00:00
}
else
if ( Item . SumPower && Item . arr . length >= Context . CountProof ) {
Item . OK = 1
Context . BlockProof = Item . arr [ 0 ]
}
}
Context . Mode ++
global . ToLog ( "Next mode: " + Context . Mode + " SumPower:" + MaxPow , 2 )
break ;
case 3 :
if ( global . TX_PROCESS && global . TX_PROCESS . RunRPC ) {
Context . Mode ++
global . ToLog ( "Next mode: " + Context . Mode , 2 )
var Block = { BlockNum : Context.BlockNumRest } ;
this . BlockNumDB = Block . BlockNum
this . BlockNumDBMin = Block . BlockNum
this . WriteBlockHeaderDB ( Block )
this . UseTruncateBlockDB = undefined
2019-07-11 05:35:16 +00:00
global . ToLog ( "Start run TXPrepareLoadRest" , 2 )
2019-07-10 04:01:15 +00:00
global . TX_PROCESS . RunRPC ( "TXPrepareLoadRest" , Block . BlockNum , function ( Err , Params ) {
Context . Mode ++
global . ToLog ( "Next mode: " + Context . Mode , 2 )
} )
}
break ;
case 4 :
break ;
case 5 :
let BlockProof = Context . BlockProof ;
var SendCount = 0 ;
if ( BlockProof )
for ( var i = 0 ; i < Context . ArrProof . length ; i ++ ) {
let Item = Context . ArrProof [ i ] ;
if ( Item . OK ) {
SendCount ++
2019-07-11 05:35:16 +00:00
global . ToLog ( "Send rest get block proof:" + BlockProof . BlockNum + " to " + global . NodeName ( Item . Node ) , 2 )
2019-07-10 04:01:15 +00:00
this . SendF ( Item . Node , {
"Method" : "GETBLOCK" , "Data" : { BlockNum : BlockProof.BlockNum , TreeHash : BlockProof.TreeHash } , "Context" : {
F : function ( Info ) {
if ( Context . TxProof )
return ;
2019-07-13 10:48:51 +00:00
var Data = global . BufLib . GetObjectFromBuffer ( Info . Data , DB_FORMAT . FORMAT_BLOCK_TRANSFER , global . WRK_BLOCK_TRANSFER ) ;
2019-07-10 04:01:15 +00:00
Info . Data = undefined
if ( Data . BlockNum !== BlockProof . BlockNum || global . CompareArr ( Data . TreeHash , BlockProof . TreeHash ) !== 0 ) {
2019-07-11 05:35:16 +00:00
global . ToLog ( "Error get proof block from " + global . NodeName ( Item . Node ) , 2 )
2019-07-10 04:01:15 +00:00
return ;
}
2019-07-10 07:59:04 +00:00
var TreeHash = global . CalcTreeHashFromArrBody ( Data . BlockNum , Data . arrContent ) ;
2019-07-10 04:01:15 +00:00
if ( global . CompareArr ( BlockProof . TreeHash , TreeHash ) !== 0 ) {
2019-07-11 05:35:16 +00:00
global . ToLog ( "Error TreeHash in proof block from " + global . NodeName ( Item . Node ) , 2 )
2019-07-10 04:01:15 +00:00
return ;
}
2019-07-11 05:35:16 +00:00
global . ToLog ( "GET BLOCK proof from " + global . NodeName ( Item . Node ) , 2 )
2019-07-10 04:01:15 +00:00
var FindTx = undefined ;
for ( var n = 0 ; n < Data . arrContent . length ; n ++ ) {
var Body = Data . arrContent [ n ] ;
2019-07-12 14:46:26 +00:00
if ( Body [ 0 ] === TYPE_TRANSACTION . TYPE_TRANSACTION_ACC_HASH ) {
2019-07-10 04:01:15 +00:00
try {
2019-07-13 10:48:51 +00:00
FindTx = global . BufLib . GetObjectFromBuffer ( Body , DB_FORMAT . FORMAT_ACCOUNT_HASH3 , { } )
2019-07-10 04:01:15 +00:00
}
catch ( e ) {
global . ToLog ( "Error parsing Body[" + n + "] block proof: " + e , 2 )
continue ;
}
break ;
}
}
if ( ! FindTx )
return ;
Context . TxProof = FindTx
Context . Mode ++
global . ToLog ( "Next mode: " + Context . Mode , 2 )
Context . AccTaskList = [ ]
Context . AccTaskFinished = 0
var AccCount = FindTx . AccountMax + 1 ;
2019-07-10 07:59:04 +00:00
for ( var n = 0 ; n < AccCount ; n += global . MAX_ACCOUNTS_TRANSFER ) {
var Task = { StartNum : n , Count : global.MAX_ACCOUNTS_TRANSFER , Time : 0 , MapSend : { } } ;
2019-07-10 04:01:15 +00:00
if ( Task . StartNum + Task . Count > AccCount )
Task . Count = AccCount - Task . StartNum
Context . AccTaskList . push ( Task )
}
Context . SmartTaskList = [ ]
Context . SmartTaskFinished = 0
2019-07-10 07:59:04 +00:00
for ( var n = 0 ; n < FindTx . SmartCount ; n += global . MAX_SMARTS_TRANSFER ) {
var Task = { StartNum : n , Count : global.MAX_SMARTS_TRANSFER , Time : 0 , MapSend : { } } ;
2019-07-10 04:01:15 +00:00
if ( Task . StartNum + Task . Count > FindTx . SmartCount )
Task . Count = FindTx . SmartCount - Task . StartNum
Context . SmartTaskList . push ( Task )
}
}
} ,
} )
if ( SendCount >= 5 )
break ;
}
}
Context . Mode ++
global . ToLog ( "Next mode: " + Context . Mode , 2 )
break ;
case 6 :
break ;
case 7 :
if ( Context . AccTaskFinished === Context . AccTaskList . length ) {
Context . Mode ++
global . ToLog ( "Next mode: " + Context . Mode , 2 )
break ;
}
var CurTime = Date . now ( ) ;
for ( var i = 0 ; i < Context . AccTaskList . length ; i ++ ) {
let Task = Context . AccTaskList [ i ] ;
var Delta = CurTime - Task . Time ;
if ( Delta > 5 * 1000 && ! Task . OK ) {
var Ret = this . GetNextNode ( Task , "" , 1 ) ;
if ( Ret . Result ) {
2019-07-11 05:35:16 +00:00
global . ToLog ( "Send GETREST Num:" + Task . StartNum + "-" + Task . Count + " to " + global . NodeName ( Ret . Node ) , 2 )
2019-07-10 04:01:15 +00:00
var SELF = this ;
this . SendF ( Ret . Node , {
"Method" : "GETREST" , "Data" : { BlockNum : Context.BlockNumRest , AccNum : Task.StartNum , Count : Task.Count , AccHash : Context.TxProof.AccHash } ,
"Context" : {
F : function ( Info ) {
if ( Task . OK )
return ;
var Data = SELF . DataFromF ( Info ) ;
if ( ! Data . Result )
return ;
if ( Data . Version !== 1 ) {
2019-07-11 05:35:16 +00:00
global . ToLog ( "ERROR Version Result GETREST Num:" + Task . StartNum + " from " + global . NodeName ( Info . Node ) , 2 )
2019-07-10 04:01:15 +00:00
return ;
}
2019-07-10 07:59:04 +00:00
if ( global . CompareArrL ( Data . ProofHash , Context . TxProof . AccHash ) !== 0 ) {
2019-07-11 05:35:16 +00:00
global . ToLog ( "ERROR PROOF HASH Result GETREST Num:" + Task . StartNum + " Hash: " + global . GetHexFromArr ( Data . ProofHash ) + "/" + global . GetHexFromArr ( Context . TxProof . AccHash ) + " from " + global . NodeName ( Info . Node ) ,
2019-07-10 04:01:15 +00:00
2 )
return ;
}
var ArrM = [ ] ;
for ( var i = 0 ; i < Data . Arr . length ; i ++ ) {
ArrM [ i ] = global . shaarr ( Data . Arr [ i ] )
}
2019-07-10 07:59:04 +00:00
var GetHash = global . CheckMerkleProof ( Data . ProofArrL , ArrM , Data . ProofArrR ) ;
if ( global . CompareArrL ( GetHash , Context . TxProof . AccHash ) !== 0 ) {
2019-07-11 05:35:16 +00:00
global . ToLog ( "ERROR CALC PROOF HASH Result GETREST Num:" + Task . StartNum + " Hash: " + global . GetHexFromArr ( GetHash ) + "/" + global . GetHexFromArr ( Context . TxProof . AccHash ) + " from " + global . NodeName ( Info . Node ) ,
2019-07-10 04:01:15 +00:00
2 )
return ;
}
2019-07-11 05:35:16 +00:00
global . ToLog ( "OK Result GETREST Num:" + Task . StartNum + " arr=" + Data . Arr . length + " from " + global . NodeName ( Info . Node ) , 2 )
2019-07-10 04:01:15 +00:00
if ( ! global . TX_PROCESS || ! global . TX_PROCESS . RunRPC ) {
global . ToLog ( "ERROR global.TX_PROCESS" )
return ;
}
Task . OK = 1
global . TX_PROCESS . RunRPC ( "TXWriteAccArr" , { StartNum : Task.StartNum , Arr : Data.Arr } , function ( Err , Params ) {
Context . AccTaskFinished ++
} )
}
} ,
} )
Task . Time = CurTime
break ;
}
}
}
break ;
case 8 :
if ( Context . SmartTaskFinished === Context . SmartTaskList . length ) {
Context . Mode ++
global . ToLog ( "Next mode: " + Context . Mode , 2 )
break ;
}
var CurTime = Date . now ( ) ;
for ( var i = 0 ; i < Context . SmartTaskList . length ; i ++ ) {
let Task = Context . SmartTaskList [ i ] ;
var Delta = CurTime - Task . Time ;
if ( Delta > 3 * 1000 && ! Task . OK ) {
var Ret = this . GetNextNode ( Task , "" , 1 ) ;
if ( Ret . Result ) {
2019-07-11 05:35:16 +00:00
global . ToLog ( "Send GETSMART Num:" + Task . StartNum + "-" + Task . Count + " to " + global . NodeName ( Ret . Node ) , 2 )
2019-07-10 04:01:15 +00:00
var SELF = this ;
this . SendF ( Ret . Node , {
"Method" : "GETSMART" , "Data" : { BlockNum : Context.BlockNumRest , SmartNum : Task.StartNum , Count : Task.Count } ,
"Context" : {
F : function ( Info ) {
if ( Task . OK )
return ;
var Data = SELF . DataFromF ( Info ) ;
if ( ! Data . Result )
return ;
2019-07-11 05:35:16 +00:00
global . ToLog ( "Result GETSMART Num:" + Task . StartNum + " arr=" + Data . Arr . length + " from " + global . NodeName ( Info . Node ) , 2 )
2019-07-10 04:01:15 +00:00
Task . Node = Info . Node
if ( ! global . TX_PROCESS || ! global . TX_PROCESS . RunRPC )
return ;
Task . OK = 1
global . TX_PROCESS . RunRPC ( "TXWriteSmartArr" , { StartNum : Task.StartNum , Arr : Data.Arr } , function ( Err , Params ) {
Context . SmartTaskFinished ++
} )
}
} ,
} )
Task . Time = CurTime
break ;
}
}
}
break ;
case 9 :
if ( ! global . TX_PROCESS || ! global . TX_PROCESS . RunRPC )
return ;
var ErrSmartNum = CheckHashSmarts ( Context . TxProof . SmartHash ) ;
if ( ErrSmartNum > 0 ) {
var Str = "Error hash in smart num: " + ErrSmartNum ;
global . ToLog ( Str , 2 )
2019-07-10 07:59:04 +00:00
var t = Math . trunc ( ErrSmartNum / global . MAX_SMARTS_TRANSFER ) ;
2019-07-10 04:01:15 +00:00
var Task = Context . SmartTaskList [ t ] ;
if ( ! Task ) {
global . ToLog ( "error task number: " + t )
Context . Mode = 100
}
else {
Task . OK = 0
Context . Mode --
Context . SmartTaskFinished --
this . AddToBan ( Task . Node , Str )
}
break ;
}
var SELF = this ;
global . TX_PROCESS . RunRPC ( "TXWriteAccHash" , { } , function ( Err , Params ) {
if ( ! Params )
return ;
if ( global . CompareArr ( Context . TxProof . AccHash , Params . AccHash ) === 0 && global . CompareArr ( Context . TxProof . SmartHash , Params . SmartHash ) === 0 ) {
Context . Mode ++
global . ToLog ( "Next mode: " + Context . Mode , 2 )
}
else {
global . ToLog ( "ERROR RESTS LOAD:" )
global . ToLog ( "Must AccHash:" + global . GetHexFromArr ( Context . TxProof . AccHash ) )
global . ToLog ( "Must SmartHash:" + global . GetHexFromArr ( Context . TxProof . SmartHash ) )
global . ToLog ( "Write AccHash:" + global . GetHexFromArr ( Params . AccHash ) )
global . ToLog ( "Write SmartHash:" + global . GetHexFromArr ( Params . SmartHash ) )
SELF . BlockNumDB = 0
SELF . BlockNumDBMin = 0
SELF . UseTruncateBlockDB = undefined
global . TX_PROCESS . RunRPC ( "TXPrepareLoadRest" , 0 , function ( Err , Params ) {
} )
Context . Mode = 100
}
} )
Context . Mode ++
global . ToLog ( "Next mode: " + Context . Mode , 2 )
break ;
case 10 :
break ;
case 11 :
var Context2 = this . LoadHistoryContext ;
Context2 . BlockNum = this . LoadRestContext . BlockNumRest
Context2 . StartTimeHistory = Date . now ( )
Context . Mode = 200
break ;
case 200 :
global . ToLog ( "Error state!" )
break ;
}
2019-07-11 05:35:16 +00:00
}
GetBlockArrFromBuffer_Load ( BufRead , Info ? ) : any {
2019-07-12 14:46:26 +00:00
//defiend in block-loader.ts(CBlock)
2019-07-10 04:01:15 +00:00
}
RETBLOCKHEADER_REST ( Info , CurTime ) {
if ( Info . Node . SendRestGetHeader === 2 )
return ;
Info . Node . SendRestGetHeader = 2
var Context = this . LoadRestContext ;
var BufRead = global . BufLib . GetReadBuffer ( Info . Data ) ;
var arr = this . GetBlockArrFromBuffer_Load ( BufRead , Info ) ;
2019-07-11 05:35:16 +00:00
global . ToLog ( "RETBLOCKHEADER_FOWARD SyncRest from " + global . NodeName ( Info . Node ) + " arr=" + arr . length , 2 )
2019-07-10 04:01:15 +00:00
Context . ReceiveHeaderCount ++
var MinSumPow = 10 * Context . CountProof ;
var SumPower = 0 ;
if ( arr . length >= Context . CountProof )
for ( var i = 0 ; i < Context . CountProof ; i ++ ) {
SumPower += arr [ i ] . Power
}
if ( SumPower <= MinSumPow )
SumPower = 0
Context . ArrProof . push ( { Node : Info.Node , SumPower : SumPower , arr : arr , BufRead : BufRead } )
}
2019-07-13 10:48:51 +00:00
static GETSMART_F() {
2019-07-10 04:01:15 +00:00
return " { \
SmartNum :uint , \
Count :uint , \
} " ;
}
2019-07-13 10:48:51 +00:00
static RETSMART_F() {
return DB_FORMAT . FORMAT_SMART_TRANSFER ;
2019-07-10 04:01:15 +00:00
}
2019-07-13 10:48:51 +00:00
static GETREST_F() {
2019-07-10 04:01:15 +00:00
return " { \
BlockNum :uint , \
AccNum :uint , \
Count :uint , \
AccHash :hash , \
} " ;
}
2019-07-13 10:48:51 +00:00
static RETREST_F() {
return DB_FORMAT . FORMAT_REST_TRANSFER ;
2019-07-10 04:01:15 +00:00
}
SendLoadToBegin() {
return ;
if ( ! this . BlockNumDBMin )
return ;
if ( ! this . ContextSendLoadToBegin )
this . ContextSendLoadToBegin = { Time : 0 , MapSend : { } }
var Context = this . ContextSendLoadToBegin ;
var CurTime = Date . now ( ) ;
var Delta = CurTime - Context . Time ;
if ( Delta < 2 * 1000 )
return ;
var BlockDB = this . ReadBlockHeaderDB ( this . BlockNumDBMin + 1 ) ;
if ( ! BlockDB )
return ;
Context . BlockNum = BlockDB . BlockNum
var Ret = this . GetNextNode ( Context , Context . BlockNum , 1 ) ;
if ( Ret . Result ) {
var Node = Ret . Node ;
2019-07-11 05:35:16 +00:00
global . ToLog ( "LOAD_TO_BEGIN - from: " + BlockDB . BlockNum + " to " + global . NodeName ( Node ) , 2 )
2019-07-10 04:01:15 +00:00
Context . Time = CurTime
this . SendF ( Node , {
"Method" : "GETBLOCKHEADER" , "Data" : { Foward : 0 , BlockNum : Context.BlockNum , Hash : BlockDB.Hash , IsSum : 0 , Count : global.COUNT_HISTORY_BLOCKS_FOR_LOAD } ,
"Context" : {
F : function ( Info ) {
2019-07-11 05:35:16 +00:00
global . ToLog ( "GET LOAD_TO_BEGIN from " + global . NodeName ( Info . Node ) + " Length=" + Info . Data . length , 2 )
2019-07-10 04:01:15 +00:00
}
}
} )
}
}
} ;
function CheckHashSmarts ( LastSumHash ) {
global . DApps . Smart . Close ( ) ;
var MaxNum = global . DApps . Smart . GetMaxNum ( ) ;
var Item = global . DApps . Smart . DBSmart . Read ( MaxNum ) ;
if ( global . CompareArr ( Item . SumHash , LastSumHash ) !== 0 )
return MaxNum ;
var WorkStruct = { } ;
for ( var Num = MaxNum ; Num >= 1 ; Num -- ) {
var PrevItem = global . DApps . Smart . DBSmart . Read ( Num - 1 ) ;
if ( ! PrevItem )
return Num ;
var WasSumHash = Item . SumHash ;
Item . SumHash = [ ] ;
2019-07-13 10:48:51 +00:00
var Buf = global . BufLib . GetBufferFromObject ( Item , DB_FORMAT . FORMAT_SMART_ROW , 20000 , WorkStruct ) ;
2019-07-10 04:01:15 +00:00
var Hash = global . sha3 ( Buf ) ;
2019-07-10 07:59:04 +00:00
var SumHash = global . sha3arr2 ( PrevItem . SumHash , Hash ) ;
2019-07-10 04:01:15 +00:00
if ( global . CompareArr ( SumHash , WasSumHash ) !== 0 )
return Num ;
Item = PrevItem ;
}
return 0 ;
} ;