indivisible_asset.js
function updateIndivisibleOutputsThatWereReceivedUnstable(conn, onDone){
	
	function updateOutputProps(unit, is_serial, onUpdated){
		// may update several outputs
		conn.query(
			"UPDATE outputs SET is_serial=? WHERE unit=?", 
			[is_serial, unit],
			function(){
				is_serial ? updateInputUniqueness(unit, onUpdated) : onUpdated();
			}
		);
	}
	
	function updateInputUniqueness(unit, onUpdated){
		// may update several inputs
		conn.query("UPDATE inputs SET is_unique=1 WHERE unit=?", [unit], function(){
			onUpdated();
		});
	}
	
	console.log("updatePrivateIndivisibleOutputsThatWereReceivedUnstable starting");
	conn.query(
		"SELECT unit, message_index, sequence FROM outputs "+(conf.storage === 'sqlite' ? "INDEXED BY outputsIsSerial" : "")+" \n\
		JOIN units USING(unit) \n\
		WHERE outputs.is_serial IS NULL AND units.is_stable=1 AND is_spent=0", // is_spent=0 selects the final output in the chain
		function(rows){
			if (rows.length === 0)
				return onDone();
			async.eachSeries(
				rows,
				function(row, cb){
					
					function updateFinalOutputProps(is_serial){
						updateOutputProps(row.unit, is_serial, cb);
					}
					
					function goUp(unit, message_index){
						// we must have exactly 1 input per message
						conn.query(
							"SELECT src_unit, src_message_index, src_output_index \n\
							FROM inputs \n\
							WHERE unit=? AND message_index=?", 
							[unit, message_index],
							function(src_rows){
								if (src_rows.length === 0)
									throw Error("updating unstable: blackbyte input not found");
								if (src_rows.length > 1)
									throw Error("updating unstable: more than one input found");
								var src_row = src_rows[0];
								if (src_row.src_unit === null) // reached root of the chain (issue)
									return cb();
								conn.query(
									"SELECT sequence, is_stable, is_serial FROM outputs JOIN units USING(unit) \n\
									WHERE unit=? AND message_index=? AND output_index=?", 
									[src_row.src_unit, src_row.src_message_index, src_row.src_output_index],
									function(prev_rows){
										if (prev_rows.length === 0)
											throw Error("src unit not found");
										var prev_output = prev_rows[0];
										if (prev_output.is_serial === 0)
											throw Error("prev is already nonserial");
										if (prev_output.is_stable === 0)
											throw Error("prev is not stable");
										if (prev_output.is_serial === 1 && prev_output.sequence !== 'good')
											throw Error("prev is_serial=1 but seq!=good");
										if (prev_output.is_serial === 1) // already was stable when initially received
											return cb();
										var is_serial = (prev_output.sequence === 'good') ? 1 : 0;
										updateOutputProps(src_row.src_unit, is_serial, function(){
											if (!is_serial) // overwrite the tip of the chain
												return updateFinalOutputProps(0);
											goUp(src_row.src_unit, src_row.src_message_index);
										});
									}
								);
							}
						);
					}
					
					var is_serial = (row.sequence === 'good') ? 1 : 0;
					updateOutputProps(row.unit, is_serial, function(){
						goUp(row.unit, row.message_index);
					});
				},
				onDone
			);
		}
	);
}
function updateIndivisibleOutputsThatWereReceivedUnstable(conn, onDone){
	
	function updateOutputProps(unit, is_serial, onUpdated){
		// may update several outputs
		conn.query(
			"UPDATE outputs SET is_serial=? WHERE unit=?", 
			[is_serial, unit],
			function(){
				is_serial ? updateInputUniqueness(unit, onUpdated) : onUpdated();
			}
		);
	}
	
	function updateInputUniqueness(unit, onUpdated){
		// may update several inputs
		conn.query("UPDATE inputs SET is_unique=1 WHERE unit=?", [unit], function(){
			onUpdated();
		});
	}
	
	console.log("updatePrivateIndivisibleOutputsThatWereReceivedUnstable starting");
	conn.query(
		"SELECT unit, message_index, sequence FROM outputs "+(conf.storage === 'sqlite' ? "INDEXED BY outputsIsSerial" : "")+" \n\
		JOIN units USING(unit) \n\
		WHERE outputs.is_serial IS NULL AND units.is_stable=1 AND is_spent=0", // is_spent=0 selects the final output in the chain
		function(rows){
			if (rows.length === 0)
				return onDone();
			async.eachSeries(
				rows,
				function(row, cb){
					
					function updateFinalOutputProps(is_serial){
						updateOutputProps(row.unit, is_serial, cb);
					}
					
					function goUp(unit, message_index){
						// we must have exactly 1 input per message
						conn.query(
							"SELECT src_unit, src_message_index, src_output_index \n\
							FROM inputs \n\
							WHERE unit=? AND message_index=?", 
							[unit, message_index],
							function(src_rows){
								if (src_rows.length === 0)
									throw Error("updating unstable: blackbyte input not found");
								if (src_rows.length > 1)
									throw Error("updating unstable: more than one input found");
								var src_row = src_rows[0];
								if (src_row.src_unit === null) // reached root of the chain (issue)
									return cb();
								conn.query(
									"SELECT sequence, is_stable, is_serial FROM outputs JOIN units USING(unit) \n\
									WHERE unit=? AND message_index=? AND output_index=?", 
									[src_row.src_unit, src_row.src_message_index, src_row.src_output_index],
									function(prev_rows){
										if (prev_rows.length === 0)
											throw Error("src unit not found");
										var prev_output = prev_rows[0];
										if (prev_output.is_serial === 0)
											throw Error("prev is already nonserial");
										if (prev_output.is_stable === 0)
											throw Error("prev is not stable");
										if (prev_output.is_serial === 1 && prev_output.sequence !== 'good')
											throw Error("prev is_serial=1 but seq!=good");
										if (prev_output.is_serial === 1) // already was stable when initially received
											return cb();
										var is_serial = (prev_output.sequence === 'good') ? 1 : 0;
										updateOutputProps(src_row.src_unit, is_serial, function(){
											if (!is_serial) // overwrite the tip of the chain
												return updateFinalOutputProps(0);
											goUp(src_row.src_unit, src_row.src_message_index);
										});
									}
								);
							}
						);
					}
					
					var is_serial = (row.sequence === 'good') ? 1 : 0;
					updateOutputProps(row.unit, is_serial, function(){
						goUp(row.unit, row.message_index);
					});
				},
				onDone
			);
		}
	);
}
function updateIndivisibleOutputsThatWereReceivedUnstable ( conn , onDone ) { function updateOutputProps ( unit , is_serial , onUpdated ) { // may update several outputs conn . query ( "UPDATE outputs SET is_serial=? WHERE unit=?" , [ is_serial , unit ] , function ( ) { is_serial ? updateInputUniqueness ( unit , onUpdated ) : onUpdated ( ) ; } ) ; } function updateInputUniqueness ( unit , onUpdated ) { // may update several inputs conn . query ( "UPDATE inputs SET is_unique=1 WHERE unit=?" , [ unit ] , function ( ) { onUpdated ( ) ; } ) ; } console . log ( "updatePrivateIndivisibleOutputsThatWereReceivedUnstable starting" ) ; conn . query ( "SELECT unit, message_index, sequence FROM outputs " + ( conf . storage === 'sqlite' ? "INDEXED BY outputsIsSerial" : "" ) + " \n\
		JOIN units USING(unit) \n\
		WHERE outputs.is_serial IS NULL AND units.is_stable=1 AND is_spent=0" , // is_spent=0 selects the final output in the chain function ( rows ) { if ( rows . length === 0 ) return onDone ( ) ; async . eachSeries ( rows , function ( row , cb ) { function updateFinalOutputProps ( is_serial ) { updateOutputProps ( row . unit , is_serial , cb ) ; } function goUp ( unit , message_index ) { // we must have exactly 1 input per message conn . query ( "SELECT src_unit, src_message_index, src_output_index \n\
							FROM inputs \n\
							WHERE unit=? AND message_index=?" , [ unit , message_index ] , function ( src_rows ) { if ( src_rows . length === 0 ) throw Error ( "updating unstable: blackbyte input not found" ) ; if ( src_rows . length > 1 ) throw Error ( "updating unstable: more than one input found" ) ; var src_row = src_rows [ 0 ] ; if ( src_row . src_unit === null ) // reached root of the chain (issue) return cb ( ) ; conn . query ( "SELECT sequence, is_stable, is_serial FROM outputs JOIN units USING(unit) \n\
									WHERE unit=? AND message_index=? AND output_index=?" , [ src_row . src_unit , src_row . src_message_index , src_row . src_output_index ] , function ( prev_rows ) { if ( prev_rows . length === 0 ) throw Error ( "src unit not found" ) ; var prev_output = prev_rows [ 0 ] ; if ( prev_output . is_serial === 0 ) throw Error ( "prev is already nonserial" ) ; if ( prev_output . is_stable === 0 ) throw Error ( "prev is not stable" ) ; if ( prev_output . is_serial === 1 && prev_output . sequence !== 'good' ) throw Error ( "prev is_serial=1 but seq!=good" ) ; if ( prev_output . is_serial === 1 ) // already was stable when initially received return cb ( ) ; var is_serial = ( prev_output . sequence === 'good' ) ? 1 : 0 ; updateOutputProps ( src_row . src_unit , is_serial , function ( ) { if ( ! is_serial ) // overwrite the tip of the chain return updateFinalOutputProps ( 0 ) ; goUp ( src_row . src_unit , src_row . src_message_index ) ; } ) ; } ) ; } ) ; } var is_serial = ( row . sequence === 'good' ) ? 1 : 0 ; updateOutputProps ( row . unit , is_serial , function ( ) { goUp ( row . unit , row . message_index ) ; } ) ; } , onDone ) ; } ) ; }
must be executed within transaction
must be executed within transaction