diff --git a/bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/BitcoinCashKit.kt b/bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/BitcoinCashKit.kt index bcc650965..da2111732 100644 --- a/bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/BitcoinCashKit.kt +++ b/bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/BitcoinCashKit.kt @@ -5,10 +5,12 @@ import android.database.sqlite.SQLiteDatabase import io.horizontalsystems.bitcoincash.blocks.BitcoinCashBlockValidatorHelper import io.horizontalsystems.bitcoincash.blocks.validators.DAAValidator import io.horizontalsystems.bitcoincash.blocks.validators.EDAValidator +import io.horizontalsystems.bitcoincash.blocks.validators.ForkValidator import io.horizontalsystems.bitcoincore.AbstractKit import io.horizontalsystems.bitcoincore.BitcoinCore import io.horizontalsystems.bitcoincore.BitcoinCoreBuilder import io.horizontalsystems.bitcoincore.blocks.validators.LegacyDifficultyAdjustmentValidator +import io.horizontalsystems.bitcoincore.extensions.toReversedByteArray import io.horizontalsystems.bitcoincore.managers.BitcoinCashAddressSelector import io.horizontalsystems.bitcoincore.managers.InsightApi import io.horizontalsystems.bitcoincore.models.TransactionInfo @@ -81,7 +83,12 @@ class BitcoinCashKit : AbstractKit { if (networkType == NetworkType.MainNet) { val blockHelper = BitcoinCashBlockValidatorHelper(storage) - bitcoinCore.addBlockValidator(DAAValidator(targetSpacing, blockHelper)) + val svForkHeight = 556767 + val abcForkBlockHash = "0000000000000000004626ff6e3b936941d341c5932ece4357eeccac44e6d56c".toReversedByteArray() + + val daaValidator = DAAValidator(targetSpacing, blockHelper) + bitcoinCore.addBlockValidator(ForkValidator(svForkHeight, abcForkBlockHash, daaValidator)) + bitcoinCore.addBlockValidator(daaValidator) bitcoinCore.addBlockValidator(LegacyDifficultyAdjustmentValidator(blockHelper, heightInterval, targetTimespan, maxTargetBits)) bitcoinCore.addBlockValidator(EDAValidator(maxTargetBits, blockHelper, network.bip44CheckpointBlock.height)) } diff --git a/bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/blocks/validators/ForkValidator.kt b/bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/blocks/validators/ForkValidator.kt new file mode 100644 index 000000000..93adf0b01 --- /dev/null +++ b/bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/blocks/validators/ForkValidator.kt @@ -0,0 +1,20 @@ +package io.horizontalsystems.bitcoincash.blocks.validators + +import io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException +import io.horizontalsystems.bitcoincore.blocks.validators.IBlockValidator +import io.horizontalsystems.bitcoincore.models.Block + +class ForkValidator(private val forkHeight: Int, private val expectedBlockHash: ByteArray, private val concreteBlockValidator: IBlockValidator) : IBlockValidator { + + override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean { + return block.height == forkHeight + } + + override fun validate(block: Block, previousBlock: Block) { + if (!block.headerHash.contentEquals(expectedBlockHash)) { + throw BlockValidatorException.WrongBlockHash(expectedBlockHash, block.headerHash) + } + + concreteBlockValidator.validate(block, previousBlock) + } +} diff --git a/bitcoincashkit/src/test/kotlin/io/horizontalsystems/bitcoincash/blocks/validators/ForkValidatorTest.kt b/bitcoincashkit/src/test/kotlin/io/horizontalsystems/bitcoincash/blocks/validators/ForkValidatorTest.kt new file mode 100644 index 000000000..d9d692134 --- /dev/null +++ b/bitcoincashkit/src/test/kotlin/io/horizontalsystems/bitcoincash/blocks/validators/ForkValidatorTest.kt @@ -0,0 +1,53 @@ +package io.horizontalsystems.bitcoincash.blocks.validators + +import com.nhaarman.mockito_kotlin.mock +import com.nhaarman.mockito_kotlin.whenever +import io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException +import io.horizontalsystems.bitcoincore.blocks.validators.IBlockValidator +import io.horizontalsystems.bitcoincore.models.Block +import org.junit.Assert +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.assertThrows +import org.spekframework.spek2.Spek +import org.spekframework.spek2.style.specification.describe + +object ForkValidatorTest : Spek({ + val forkHeight = 100 + val expectedBlockHash = byteArrayOf(1, 2, 3) + val concreteBlockValidator = mock() + val validator by memoized { ForkValidator(forkHeight, expectedBlockHash, concreteBlockValidator) } + + describe("#isBlockValidatable") { + val block = mock() + + it("is true when block height is equal to fork height") { + whenever(block.height).thenReturn(forkHeight) + Assert.assertTrue(validator.isBlockValidatable(block, mock())) + } + + it("is false when block height is not equal to fork height") { + whenever(block.height).thenReturn(104) + Assert.assertFalse(validator.isBlockValidatable(block, mock())) + } + + } + + describe("#validate") { + val block = mock() + + it("validates without any error when block hash is equal to expected block hash") { + whenever(block.headerHash).thenReturn(expectedBlockHash) + Assertions.assertDoesNotThrow { + validator.validate(block, mock()) + } + } + + it("throws exception when block hash is not equal to expected block hash") { + whenever(block.headerHash).thenReturn(byteArrayOf(3, 2, 1)) + assertThrows { + validator.validate(block, mock()) + } + } + } + +}) diff --git a/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/InitialBlockDownload.kt b/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/InitialBlockDownload.kt index 30fd7a666..24e650b5b 100644 --- a/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/InitialBlockDownload.kt +++ b/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/InitialBlockDownload.kt @@ -167,8 +167,10 @@ class InitialBlockDownload( syncedPeers.add(peer) blockSyncer.downloadCompleted() - syncStateListener.onSyncFinish() - peer.sendMempoolMessage() + if (peerManager.isHalfSynced()) { + syncStateListener.onSyncFinish() + } + peer.sendMempoolMessage() logger.info("Peer synced ${peer.host}") syncPeer = null assignNextSyncPeer() diff --git a/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/BlockValidatorException.kt b/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/BlockValidatorException.kt index c2c24862c..a243579e2 100644 --- a/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/BlockValidatorException.kt +++ b/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/BlockValidatorException.kt @@ -1,5 +1,7 @@ package io.horizontalsystems.bitcoincore.blocks.validators +import io.horizontalsystems.bitcoincore.extensions.toReversedHex + open class BlockValidatorException(msg: String) : RuntimeException(msg) { class NoHeader : BlockValidatorException("No Header") class NoCheckpointBlock : BlockValidatorException("No Checkpoint Block") @@ -8,4 +10,5 @@ open class BlockValidatorException(msg: String) : RuntimeException(msg) { class NotEqualBits : BlockValidatorException("Not Equal Bits") class NotDifficultyTransitionEqualBits : BlockValidatorException("Not Difficulty Transition Equal Bits") class InvalidProofOfWork : BlockValidatorException("Invalid Prove of Work") + class WrongBlockHash(expected: ByteArray, actual: ByteArray) : BlockValidatorException("Wrong Block Hash ${actual.toReversedHex()} vs expected ${expected.toReversedHex()}") } diff --git a/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/KitStateProvider.kt b/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/KitStateProvider.kt index 963de29d7..07581c04b 100644 --- a/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/KitStateProvider.kt +++ b/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/KitStateProvider.kt @@ -60,10 +60,9 @@ class KitStateProvider : ISyncStateListener { } if (progress >= 1) { - syncState = KitState.Synced + syncState = KitState.Syncing(1.0) } else { syncState = KitState.Syncing(progress) } - } }