@ -1,20 +1,25 @@
package com.xsn.explorer.processors
import com.alexitc.playsonify.core.FutureApplicationResult
import com.alexitc.playsonify.models._
import com.xsn.explorer.data.anorm.dao. { BalancePostgresDAO , BlockPostgresDAO , StatisticsPostgresDAO , TransactionPostgresDAO }
import com.xsn.explorer.data.anorm.interpreters.FieldOrderingSQLInterpreter
import com.xsn.explorer.data.anorm. { BalancePostgresDataHandler , BlockPostgresDataHandler , DatabasePostgresSeeder , StatisticsPostgresDataHandler }
import com.xsn.explorer.data.async. { BlockFutureDataHandler , DatabaseFutureSeeder }
import com.xsn.explorer.data.common.PostgresDataHandlerSpec
import com.xsn.explorer.errors. { BlockNotFoundError , TransactionNotFoundError }
import com.xsn.explorer.helpers. { BlockLoader , Executors , FileBasedXSNService }
import com.xsn.explorer.models.fields.BalanceField
import com.xsn.explorer.models.rpc.Block
import com.xsn.explorer.processors.BlockEventsProcessor. { NewBlockAppended , RechainDone }
import com.xsn.explorer.models.rpc. { Block , Transaction }
import com.xsn.explorer.models. { Blockhash , TransactionId }
import com.xsn.explorer.processors.BlockEventsProcessor. { MissingBlockIgnored , NewBlockAppended , RechainDone , ReplacedByBlockHeight }
import com.xsn.explorer.services.TransactionService
import org.scalactic.Good
import org.scalactic. { Bad , Good }
import org.scalatest.BeforeAndAfter
import org.scalatest.concurrent.ScalaFutures
import scala.concurrent.Future
class BlockEventsProcessorSpec extends PostgresDataHandlerSpec with ScalaFutures with BeforeAndAfter {
lazy val dataHandler = new BlockPostgresDataHandler ( database , new BlockPostgresDAO )
@ -167,6 +172,123 @@ class BlockEventsProcessorSpec extends PostgresDataHandlerSpec with ScalaFutures
balance . spent mustEqual BigDecimal ( 0 )
}
}
"ignore orphan block on rare rechain events when the rpc server doesn't have the block anymore" in {
// see https : // github . com / X9Developers / block - explorer / issues / 6
val block1 = BlockLoader . get ( "000003fb382f6892ae96594b81aa916a8923c70701de4e7054aac556c7271ef7" )
val block2 = BlockLoader . get ( "000004645e2717b556682e3c642a4c6e473bf25c653ff8e8c114a3006040ffb8" )
val block3 = BlockLoader . get ( "00000766115b26ecbc09cd3a3db6870fdaf2f049d65a910eb2f2b48b566ca7bd" )
val xsnService = new FileBasedXSNService {
override def getBlock ( blockhash : Blockhash ) : FutureApplicationResult [ Block ] = {
if ( blockhash == block3 . hash ) {
Future . successful ( Bad ( BlockNotFoundError ) . accumulating )
} else {
super . getBlock ( blockhash )
}
}
}
val processor = new BlockEventsProcessor (
xsnService ,
new TransactionService ( xsnService ) ( Executors . globalEC ) ,
new DatabaseFutureSeeder ( dataSeeder ) ( Executors . databaseEC ) ,
new BlockFutureDataHandler ( dataHandler ) ( Executors . databaseEC ) )
List ( block1 , block2 )
. map ( _ . hash )
. map ( processor . newLatestBlock )
. foreach { whenReady ( _ ) { _ . isGood mustEqual true } }
/* *
* When processing the latest block , a rechain event has occurred in the rpc server which leads to
* a case that we can 't retrieve the block information , the block should be ignored .
*/
whenReady ( processor . newLatestBlock ( block3 . hash ) ) { result =>
result mustEqual Good ( MissingBlockIgnored )
val blocks = List ( block1 , block2 )
verifyBlockchain ( blocks )
}
}
"ignore orphan block on rare rechain events when the rpc server doesn't have the a transaction from the block anymore" in {
// see https : // github . com / X9Developers / block - explorer / issues / 6
val block1 = BlockLoader . get ( "000003fb382f6892ae96594b81aa916a8923c70701de4e7054aac556c7271ef7" )
val block2 = BlockLoader . get ( "000004645e2717b556682e3c642a4c6e473bf25c653ff8e8c114a3006040ffb8" )
val block3 = BlockLoader . get ( "00000766115b26ecbc09cd3a3db6870fdaf2f049d65a910eb2f2b48b566ca7bd" )
val xsnService = new FileBasedXSNService {
override def getTransaction ( txid : TransactionId ) : FutureApplicationResult [ Transaction ] = {
if ( txid == block3 . transactions . last ) {
Future . successful ( Bad ( TransactionNotFoundError ) . accumulating )
} else {
super . getTransaction ( txid )
}
}
}
val processor = new BlockEventsProcessor (
xsnService ,
new TransactionService ( xsnService ) ( Executors . globalEC ) ,
new DatabaseFutureSeeder ( dataSeeder ) ( Executors . databaseEC ) ,
new BlockFutureDataHandler ( dataHandler ) ( Executors . databaseEC ) )
List ( block1 , block2 )
. map ( _ . hash )
. map ( processor . newLatestBlock )
. foreach { whenReady ( _ ) { _ . isGood mustEqual true } }
/* *
* When processing the latest block , a rechain event has occurred in the rpc server which leads to
* a case that we can 't retrieve a specific transaction , the block should be ignored .
*/
whenReady ( processor . newLatestBlock ( block3 . hash ) ) { result =>
result mustEqual Good ( MissingBlockIgnored )
val blocks = List ( block1 , block2 )
verifyBlockchain ( blocks )
}
}
"remove orphan block on rare rechain events when the block height already exists" in {
// see https : // github . com / X9Developers / block - explorer / issues / 6
val block1 = BlockLoader . get ( "000003fb382f6892ae96594b81aa916a8923c70701de4e7054aac556c7271ef7" )
val block2 = BlockLoader . get ( "000004645e2717b556682e3c642a4c6e473bf25c653ff8e8c114a3006040ffb8" )
val block3 = BlockLoader . get ( "00000766115b26ecbc09cd3a3db6870fdaf2f049d65a910eb2f2b48b566ca7bd" )
. copy ( height = block2 . height )
. copy ( previousBlockhash = block2 . previousBlockhash )
val xsnService = new FileBasedXSNService {
override def getBlock ( blockhash : Blockhash ) : FutureApplicationResult [ Block ] = {
if ( blockhash == block3 . hash ) {
Future . successful ( Good ( block3 ) )
} else {
super . getBlock ( blockhash )
}
}
}
val processor = new BlockEventsProcessor (
xsnService ,
new TransactionService ( xsnService ) ( Executors . globalEC ) ,
new DatabaseFutureSeeder ( dataSeeder ) ( Executors . databaseEC ) ,
new BlockFutureDataHandler ( dataHandler ) ( Executors . databaseEC ) )
List ( block1 , block2 )
. map ( _ . hash )
. map ( processor . newLatestBlock )
. foreach { whenReady ( _ ) { _ . isGood mustEqual true } }
whenReady ( processor . newLatestBlock ( block3 . hash ) ) { result =>
result mustEqual Good ( ReplacedByBlockHeight )
val blocks = List ( block1 , block3 )
verifyBlockchain ( blocks )
// ensure block2 has been removed
dataHandler . getBy ( block2 . hash ) mustEqual Bad ( BlockNotFoundError ) . accumulating
}
}
}
private def verifyBlockchain ( blocks : List [ Block ] ) = {