catchup/catchup.js
function readHashTree( hashTreeRequest, callbacks )
{
	let from_ball	= hashTreeRequest.from_ball;
	let to_ball	= hashTreeRequest.to_ball;

	if ( 'string' !== typeof from_ball )
	{
		return callbacks.ifError( "no from_ball" );
	}
	if ( 'string' !== typeof to_ball )
	{
		return callbacks.ifError( "no to_ball" );
	}

	let from_mci;
	let to_mci;

	_db.query
	(
		"SELECT is_stable, is_on_main_chain, main_chain_index, ball FROM balls JOIN units USING(unit) WHERE ball IN(?,?)", 
		[ from_ball, to_ball ],
		function( rows )
		{
			if ( rows.length !== 2 )
			{
				return callbacks.ifError( "some balls not found" );
			}

			for ( let i = 0; i < rows.length; i++ )
			{
				let props = rows[ i ];
				if ( props.is_stable !== 1 )
				{
					return callbacks.ifError( "some balls not stable" );
				}
				if ( props.is_on_main_chain !== 1 )
				{
					return callbacks.ifError( "some balls not on mc" );
				}

				if ( props.ball === from_ball )
				{
					from_mci = props.main_chain_index;
				}
				else if ( props.ball === to_ball )
				{
					to_mci = props.main_chain_index;
				}
			}

			if ( from_mci >= to_mci )
			{
				return callbacks.ifError( "from is after to" );
			}

			let arrBalls = [];
			let op = ( from_mci === 0 ) ? ">=" : ">";	//	if starting from 0, add genesis itself

			_db.query
			(
				"SELECT unit, ball, content_hash FROM units LEFT JOIN balls USING(unit) \n\
				WHERE main_chain_index " + op + " ? AND main_chain_index<=? ORDER BY `level`",
				[ from_mci, to_mci ],
				function( ball_rows )
				{
					_async.eachSeries
					(
						ball_rows,
						function( objBall, cb )
						{
							if ( ! objBall.ball )
							{
								throw Error( "no ball for unit " + objBall.unit );
							}
							if ( objBall.content_hash )
							{
								objBall.is_nonserial = true;
							}

							//	...
							delete objBall.content_hash;

							_db.query
							(
								"SELECT ball FROM parenthoods LEFT JOIN balls ON parent_unit=balls.unit WHERE child_unit=? ORDER BY ball", 
								[ objBall.unit ],
								function( parent_rows )
								{
									if ( parent_rows.some(function(parent_row){ return ! parent_row.ball; }))
									{
										throw Error( "some parents have no balls" );
									}
									if ( parent_rows.length > 0 )
									{
										objBall.parent_balls = parent_rows.map(function(parent_row){ return parent_row.ball; });
									}

									//
									//	just collect skiplist balls
									//
									_db.query
									(
										"SELECT ball FROM skiplist_units LEFT JOIN balls ON skiplist_unit=balls.unit WHERE skiplist_units.unit=? ORDER BY ball", 
										[ objBall.unit ],
										function( srows )
										{
											if ( srows.some(function(srow){ return ! srow.ball; }))
											{
												throw Error("some skiplist units have no balls");
											}
											if ( srows.length > 0)
											{
												objBall.skiplist_balls = srows.map(function(srow){ return srow.ball; });
											}

											//	...
											arrBalls.push( objBall );

											//	...
											cb();
										}
									);
								}
							);
						},
						function()
						{
							callbacks.ifOk( arrBalls );
						}
					);
				}
			);
		}
	);
}
function readHashTree( hashTreeRequest, callbacks )
{
	let from_ball	= hashTreeRequest.from_ball;
	let to_ball	= hashTreeRequest.to_ball;

	if ( 'string' !== typeof from_ball )
	{
		return callbacks.ifError( "no from_ball" );
	}
	if ( 'string' !== typeof to_ball )
	{
		return callbacks.ifError( "no to_ball" );
	}

	let from_mci;
	let to_mci;

	_db.query
	(
		"SELECT is_stable, is_on_main_chain, main_chain_index, ball FROM balls JOIN units USING(unit) WHERE ball IN(?,?)", 
		[ from_ball, to_ball ],
		function( rows )
		{
			if ( rows.length !== 2 )
			{
				return callbacks.ifError( "some balls not found" );
			}

			for ( let i = 0; i < rows.length; i++ )
			{
				let props = rows[ i ];
				if ( props.is_stable !== 1 )
				{
					return callbacks.ifError( "some balls not stable" );
				}
				if ( props.is_on_main_chain !== 1 )
				{
					return callbacks.ifError( "some balls not on mc" );
				}

				if ( props.ball === from_ball )
				{
					from_mci = props.main_chain_index;
				}
				else if ( props.ball === to_ball )
				{
					to_mci = props.main_chain_index;
				}
			}

			if ( from_mci >= to_mci )
			{
				return callbacks.ifError( "from is after to" );
			}

			let arrBalls = [];
			let op = ( from_mci === 0 ) ? ">=" : ">";	//	if starting from 0, add genesis itself

			_db.query
			(
				"SELECT unit, ball, content_hash FROM units LEFT JOIN balls USING(unit) \n\
				WHERE main_chain_index " + op + " ? AND main_chain_index<=? ORDER BY `level`",
				[ from_mci, to_mci ],
				function( ball_rows )
				{
					_async.eachSeries
					(
						ball_rows,
						function( objBall, cb )
						{
							if ( ! objBall.ball )
							{
								throw Error( "no ball for unit " + objBall.unit );
							}
							if ( objBall.content_hash )
							{
								objBall.is_nonserial = true;
							}

							//	...
							delete objBall.content_hash;

							_db.query
							(
								"SELECT ball FROM parenthoods LEFT JOIN balls ON parent_unit=balls.unit WHERE child_unit=? ORDER BY ball", 
								[ objBall.unit ],
								function( parent_rows )
								{
									if ( parent_rows.some(function(parent_row){ return ! parent_row.ball; }))
									{
										throw Error( "some parents have no balls" );
									}
									if ( parent_rows.length > 0 )
									{
										objBall.parent_balls = parent_rows.map(function(parent_row){ return parent_row.ball; });
									}

									//
									//	just collect skiplist balls
									//
									_db.query
									(
										"SELECT ball FROM skiplist_units LEFT JOIN balls ON skiplist_unit=balls.unit WHERE skiplist_units.unit=? ORDER BY ball", 
										[ objBall.unit ],
										function( srows )
										{
											if ( srows.some(function(srow){ return ! srow.ball; }))
											{
												throw Error("some skiplist units have no balls");
											}
											if ( srows.length > 0)
											{
												objBall.skiplist_balls = srows.map(function(srow){ return srow.ball; });
											}

											//	...
											arrBalls.push( objBall );

											//	...
											cb();
										}
									);
								}
							);
						},
						function()
						{
							callbacks.ifOk( arrBalls );
						}
					);
				}
			);
		}
	);
}
function readHashTree ( hashTreeRequest , callbacks ) { let from_ball = hashTreeRequest . from_ball ; let to_ball = hashTreeRequest . to_ball ; if ( 'string' !== typeof from_ball ) { return callbacks . ifError ( "no from_ball" ) ; } if ( 'string' !== typeof to_ball ) { return callbacks . ifError ( "no to_ball" ) ; } let from_mci ; let to_mci ; _db . query ( "SELECT is_stable, is_on_main_chain, main_chain_index, ball FROM balls JOIN units USING(unit) WHERE ball IN(?,?)" , [ from_ball , to_ball ] , function ( rows ) { if ( rows . length !== 2 ) { return callbacks . ifError ( "some balls not found" ) ; } for ( let i = 0 ; i < rows . length ; i ++ ) { let props = rows [ i ] ; if ( props . is_stable !== 1 ) { return callbacks . ifError ( "some balls not stable" ) ; } if ( props . is_on_main_chain !== 1 ) { return callbacks . ifError ( "some balls not on mc" ) ; } if ( props . ball === from_ball ) { from_mci = props . main_chain_index ; } else if ( props . ball === to_ball ) { to_mci = props . main_chain_index ; } } if ( from_mci >= to_mci ) { return callbacks . ifError ( "from is after to" ) ; } let arrBalls = [ ] ; let op = ( from_mci === 0 ) ? ">=" : ">" ; //	if starting from 0, add genesis itself _db . query ( "SELECT unit, ball, content_hash FROM units LEFT JOIN balls USING(unit) \n\
				WHERE main_chain_index " + op + " ? AND main_chain_index<=? ORDER BY `level`" , [ from_mci , to_mci ] , function ( ball_rows ) { _async . eachSeries ( ball_rows , function ( objBall , cb ) { if ( ! objBall . ball ) { throw Error ( "no ball for unit " + objBall . unit ) ; } if ( objBall . content_hash ) { objBall . is_nonserial = true ; } //	... delete objBall . content_hash ; _db . query ( "SELECT ball FROM parenthoods LEFT JOIN balls ON parent_unit=balls.unit WHERE child_unit=? ORDER BY ball" , [ objBall . unit ] , function ( parent_rows ) { if ( parent_rows . some ( function ( parent_row ) { return ! parent_row . ball ; } ) ) { throw Error ( "some parents have no balls" ) ; } if ( parent_rows . length > 0 ) { objBall . parent_balls = parent_rows . map ( function ( parent_row ) { return parent_row . ball ; } ) ; } // //	just collect skiplist balls // _db . query ( "SELECT ball FROM skiplist_units LEFT JOIN balls ON skiplist_unit=balls.unit WHERE skiplist_units.unit=? ORDER BY ball" , [ objBall . unit ] , function ( srows ) { if ( srows . some ( function ( srow ) { return ! srow . ball ; } ) ) { throw Error ( "some skiplist units have no balls" ) ; } if ( srows . length > 0 ) { objBall . skiplist_balls = srows . map ( function ( srow ) { return srow . ball ; } ) ; } //	... arrBalls . push ( objBall ) ; //	... cb ( ) ; } ) ; } ) ; } , function ( ) { callbacks . ifOk ( arrBalls ) ; } ) ; } ) ; } ) ; }
read hash tree
@param	{object}	hashTreeRequest
@param	{object}	callbacks
@return {*}
read hash tree