pow/pow.js
function calculatePublicSeedByRoundIndex( oConn, nRoundIndex, pfnCallback )
{
	if ( ! oConn )
	{
		return pfnCallback( `call calculatePublicSeedByRoundIndex with invalid oConn` );
	}
	if ( 'number' !== typeof nRoundIndex )
	{
		return pfnCallback( `call calculatePublicSeedByRoundIndex with invalid nRoundIndex` );
	}
	if ( nRoundIndex <= 1 )
	{
		//
		//	round 1
		//		hard code
		//
		return pfnCallback( null, _blakejs.blake2sHex( _constants.GENESIS_UNIT ) );
	}

	let sPreviousPublicSeed		= null;
	let arrPrePreviousCoinBase	= null;
	let sPreviousTrustMEBall	= null;

	_async.series
	([
		function( pfnNext )
		{
			//	public seed
			queryPublicSeedByRoundIndex( oConn, nRoundIndex - 1, function( err, sSeed )
			{
				if ( err )
				{
					return pfnNext( err );
				}
				if ( 'string' !== typeof sSeed || 0 === sSeed.length )
				{
					return pfnNext( `calculatePublicSeedByRoundIndex got invalid sSeed.` );
				}

				sPreviousPublicSeed = sSeed;
				return pfnNext();
			} );
		},
		function( pfnNext )
		{
			//	coin base
			if ( 2 === nRoundIndex )
			{
				arrPrePreviousCoinBase = [];
				return pfnNext();
			}

			//	...
			_round.queryCoinBaseListByRoundIndex( oConn, nRoundIndex - 1, function( err, arrCoinBaseList )
			{
				if ( err )
				{
					return pfnNext( err );
				}
				if ( ! Array.isArray( arrCoinBaseList ) )
				{
					return pfnNext( 'empty coin base list' );
				}
				if ( _constants.COUNT_WITNESSES !== arrCoinBaseList.length )
				{
					return pfnNext( 'no enough coin base units.' );
				}

				arrPrePreviousCoinBase = arrCoinBaseList;
				return pfnNext();
			} );
		},
		function( pfnNext )
		{
			//	first ball
			_round.queryFirstTrustMEBallOnMainChainByRoundIndex( oConn, nRoundIndex - 1, function( err, sBall )
			{
				if ( err )
				{
					return pfnNext( err );
				}
				if ( 'string' !== typeof sBall || 0 === sBall.length )
				{
					return pfnNext( `calculatePublicSeedByRoundIndex got invalid sBall.` );
				}

				sPreviousTrustMEBall = sBall;
				return pfnNext();
			} );
		}
	], function( err )
	{
		if ( err )
		{
			return pfnCallback( err );
		}

		//	...
		let sSource = ""
		+ sPreviousPublicSeed
		+ _crypto.createHash( 'sha512' ).update( JSON.stringify( arrPrePreviousCoinBase ), 'utf8' ).digest();
		+ _crypto.createHash( 'sha512' ).update( sPreviousTrustMEBall, 'utf8' ).digest();

		pfnCallback( null, _blakejs.blake2sHex( sSource ) );
	});
}
function calculatePublicSeedByRoundIndex( oConn, nRoundIndex, pfnCallback )
{
	if ( ! oConn )
	{
		return pfnCallback( `call calculatePublicSeedByRoundIndex with invalid oConn` );
	}
	if ( 'number' !== typeof nRoundIndex )
	{
		return pfnCallback( `call calculatePublicSeedByRoundIndex with invalid nRoundIndex` );
	}
	if ( nRoundIndex <= 1 )
	{
		//
		//	round 1
		//		hard code
		//
		return pfnCallback( null, _blakejs.blake2sHex( _constants.GENESIS_UNIT ) );
	}

	let sPreviousPublicSeed		= null;
	let arrPrePreviousCoinBase	= null;
	let sPreviousTrustMEBall	= null;

	_async.series
	([
		function( pfnNext )
		{
			//	public seed
			queryPublicSeedByRoundIndex( oConn, nRoundIndex - 1, function( err, sSeed )
			{
				if ( err )
				{
					return pfnNext( err );
				}
				if ( 'string' !== typeof sSeed || 0 === sSeed.length )
				{
					return pfnNext( `calculatePublicSeedByRoundIndex got invalid sSeed.` );
				}

				sPreviousPublicSeed = sSeed;
				return pfnNext();
			} );
		},
		function( pfnNext )
		{
			//	coin base
			if ( 2 === nRoundIndex )
			{
				arrPrePreviousCoinBase = [];
				return pfnNext();
			}

			//	...
			_round.queryCoinBaseListByRoundIndex( oConn, nRoundIndex - 1, function( err, arrCoinBaseList )
			{
				if ( err )
				{
					return pfnNext( err );
				}
				if ( ! Array.isArray( arrCoinBaseList ) )
				{
					return pfnNext( 'empty coin base list' );
				}
				if ( _constants.COUNT_WITNESSES !== arrCoinBaseList.length )
				{
					return pfnNext( 'no enough coin base units.' );
				}

				arrPrePreviousCoinBase = arrCoinBaseList;
				return pfnNext();
			} );
		},
		function( pfnNext )
		{
			//	first ball
			_round.queryFirstTrustMEBallOnMainChainByRoundIndex( oConn, nRoundIndex - 1, function( err, sBall )
			{
				if ( err )
				{
					return pfnNext( err );
				}
				if ( 'string' !== typeof sBall || 0 === sBall.length )
				{
					return pfnNext( `calculatePublicSeedByRoundIndex got invalid sBall.` );
				}

				sPreviousTrustMEBall = sBall;
				return pfnNext();
			} );
		}
	], function( err )
	{
		if ( err )
		{
			return pfnCallback( err );
		}

		//	...
		let sSource = ""
		+ sPreviousPublicSeed
		+ _crypto.createHash( 'sha512' ).update( JSON.stringify( arrPrePreviousCoinBase ), 'utf8' ).digest();
		+ _crypto.createHash( 'sha512' ).update( sPreviousTrustMEBall, 'utf8' ).digest();

		pfnCallback( null, _blakejs.blake2sHex( sSource ) );
	});
}
function calculatePublicSeedByRoundIndex ( oConn , nRoundIndex , pfnCallback ) { if ( ! oConn ) { return pfnCallback ( ` ` ) ; } if ( 'number' !== typeof nRoundIndex ) { return pfnCallback ( ` ` ) ; } if ( nRoundIndex <= 1 ) { // //	round 1 //		hard code // return pfnCallback ( null , _blakejs . blake2sHex ( _constants . GENESIS_UNIT ) ) ; } let sPreviousPublicSeed = null ; let arrPrePreviousCoinBase = null ; let sPreviousTrustMEBall = null ; _async . series ( [ function ( pfnNext ) { //	public seed queryPublicSeedByRoundIndex ( oConn , nRoundIndex - 1 , function ( err , sSeed ) { if ( err ) { return pfnNext ( err ) ; } if ( 'string' !== typeof sSeed || 0 === sSeed . length ) { return pfnNext ( ` ` ) ; } sPreviousPublicSeed = sSeed ; return pfnNext ( ) ; } ) ; } , function ( pfnNext ) { //	coin base if ( 2 === nRoundIndex ) { arrPrePreviousCoinBase = [ ] ; return pfnNext ( ) ; } //	... _round . queryCoinBaseListByRoundIndex ( oConn , nRoundIndex - 1 , function ( err , arrCoinBaseList ) { if ( err ) { return pfnNext ( err ) ; } if ( ! Array . isArray ( arrCoinBaseList ) ) { return pfnNext ( 'empty coin base list' ) ; } if ( _constants . COUNT_WITNESSES !== arrCoinBaseList . length ) { return pfnNext ( 'no enough coin base units.' ) ; } arrPrePreviousCoinBase = arrCoinBaseList ; return pfnNext ( ) ; } ) ; } , function ( pfnNext ) { //	first ball _round . queryFirstTrustMEBallOnMainChainByRoundIndex ( oConn , nRoundIndex - 1 , function ( err , sBall ) { if ( err ) { return pfnNext ( err ) ; } if ( 'string' !== typeof sBall || 0 === sBall . length ) { return pfnNext ( ` ` ) ; } sPreviousTrustMEBall = sBall ; return pfnNext ( ) ; } ) ; } ] , function ( err ) { if ( err ) { return pfnCallback ( err ) ; } //	... let sSource = "" + sPreviousPublicSeed + _crypto . createHash ( 'sha512' ) . update ( JSON . stringify ( arrPrePreviousCoinBase ) , 'utf8' ) . digest ( ) ; + _crypto . createHash ( 'sha512' ) . update ( sPreviousTrustMEBall , 'utf8' ) . digest ( ) ; pfnCallback ( null , _blakejs . blake2sHex ( sSource ) ) ; } ) ; }
calculate public seed by round index

@param	{handle}	oConn
@param	{function}	oConn.query
@param	{number}	nRoundIndex
round 1
hard code
round 2
previous seed
[]
TrustME Ball
round	3
previous seed
[]
TrustME Ball

@param	{function}	pfnCallback( err, sSeed )

@documentation
https://github.com/trustnote/document/blob/master/TrustNote-TR-2018-02.md#PoW-Unit

pubSeed(i)	= blake2s256
(
pubSeed(i-1) + hash( Coin-base(i-2) ) + hash( FirstStableMCUnit(i-1) )
)
calculate public seed by round index