p) {
29 | return (this.elementLeft.equals(p.getLeft())) && (this.elementRight.equals(p.getRight()));
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/crypto/schnorr/Util.java:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.crypto.schnorr;
2 |
3 | import java.math.BigInteger;
4 | import java.security.MessageDigest;
5 | import java.security.NoSuchAlgorithmException;
6 | import java.util.Arrays;
7 |
8 | public class Util {
9 |
10 | private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
11 |
12 | public static byte[] bytesFromBigInteger(BigInteger n) {
13 |
14 | byte[] b = n.toByteArray();
15 |
16 | if(b.length == 32) {
17 | return b;
18 | }
19 | else if(b.length > 32) {
20 | return Arrays.copyOfRange(b, b.length - 32, b.length);
21 | }
22 | else {
23 | byte[] buf = new byte[32];
24 | System.arraycopy(b, 0, buf, buf.length - b.length, b.length);
25 | return buf;
26 | }
27 | }
28 |
29 | public static BigInteger bigIntFromBytes(byte[] b) {
30 | return new BigInteger(1, b);
31 | }
32 |
33 | public static byte[] sha256(byte[] b) throws NoSuchAlgorithmException {
34 | MessageDigest digest = MessageDigest.getInstance("SHA-256");
35 | return digest.digest(b);
36 | }
37 |
38 | public static byte[] xor(byte[] b0, byte[] b1) {
39 |
40 | if(b0.length != b1.length) {
41 | return null;
42 | }
43 |
44 | byte[] ret = new byte[b0.length];
45 | int i = 0;
46 | for (byte b : b0) {
47 | ret[i] = (byte)(b ^ b1[i]);
48 | i++;
49 | }
50 |
51 | return ret;
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/exceptions/AddressFormatException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2014 Ronald W Hoffman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.horizontalsystems.bitcoincore.exceptions;
17 |
18 | /**
19 | * AddressFormatException is thrown if an invalid Bitcoin address is detected. An
20 | * address is invalid if it is not 20-bytes, has an incorrect version, or the
21 | * checksum is not valid.
22 | */
23 | public class AddressFormatException extends BitcoinException {
24 |
25 | public AddressFormatException(String msg) {
26 | super(msg);
27 | }
28 |
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/exceptions/BitcoinException.java:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.exceptions;
2 |
3 | /**
4 | * Base exception for bitcoin app.
5 | *
6 | * @author liaoxuefeng
7 | */
8 | public class BitcoinException extends RuntimeException {
9 |
10 | public BitcoinException() {
11 | }
12 |
13 | public BitcoinException(String message) {
14 | super(message);
15 | }
16 |
17 | public BitcoinException(Throwable cause) {
18 | super(cause);
19 | }
20 |
21 | public BitcoinException(String message, Throwable cause) {
22 | super(message, cause);
23 | }
24 |
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/io/BitcoinInputMarkable.java:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.io;
2 |
3 | import java.io.ByteArrayInputStream;
4 | import java.io.IOException;
5 |
6 | /**
7 | * A child class of BitcoinInput and extends its functionality.
8 | * @see BitcoinInput
9 | */
10 | public final class BitcoinInputMarkable extends BitcoinInput {
11 | //Stores the length of the data byte array.
12 | public int count;
13 | /**
14 | @param data Byte array that is going to by read by the InputStream
15 | */
16 | public BitcoinInputMarkable(byte[] data) {
17 | super(new ByteArrayInputStream(data));
18 | this.count = data.length;
19 | }
20 |
21 | /**
22 | *Marks the InputStream
23 | */
24 | public void mark() {
25 | // since the readlimit for ByteArrayInputStream has no meaning set it to 0
26 | in.mark(0);
27 | }
28 | /**
29 | *Resets the InputStream
30 | */
31 | public void reset() throws IOException {
32 | in.reset();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/BiApiTransactionProvider.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.apisync
2 |
3 | import io.horizontalsystems.bitcoincore.apisync.model.TransactionItem
4 | import io.horizontalsystems.bitcoincore.core.IApiTransactionProvider
5 | import io.horizontalsystems.bitcoincore.managers.ApiSyncStateManager
6 |
7 | class BiApiTransactionProvider(
8 | private val restoreProvider: IApiTransactionProvider,
9 | private val syncProvider: IApiTransactionProvider,
10 | private val syncStateManager: ApiSyncStateManager
11 | ) : IApiTransactionProvider {
12 |
13 | override fun transactions(addresses: List, stopHeight: Int?): List =
14 | if (syncStateManager.restored) {
15 | syncProvider.transactions(addresses, stopHeight)
16 | } else {
17 | restoreProvider.transactions(addresses, stopHeight)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/BlockHashFetcher.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.apisync
2 |
3 | import io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairBlockHashFetcher
4 | import io.horizontalsystems.bitcoincore.apisync.blockchair.IBlockHashFetcher
5 |
6 | class BlockHashFetcher(
7 | private val hsBlockHashFetcher: HsBlockHashFetcher,
8 | private val blockchairBlockHashFetcher: BlockchairBlockHashFetcher,
9 | private val checkpointHeight: Int
10 | ) : IBlockHashFetcher {
11 |
12 | override fun fetch(heights: List): Map {
13 | val beforeCheckpoint = heights.filter { it <= checkpointHeight }
14 | val afterCheckpoint = heights.filter { it > checkpointHeight }
15 |
16 | val blockHashes = mutableMapOf()
17 | if (beforeCheckpoint.isNotEmpty()) {
18 | blockHashes += hsBlockHashFetcher.fetch(beforeCheckpoint)
19 | }
20 | if (afterCheckpoint.isNotEmpty()) {
21 | blockHashes += blockchairBlockHashFetcher.fetch(afterCheckpoint)
22 | }
23 |
24 | return blockHashes
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/HsBlockHashFetcher.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.apisync
2 |
3 | import io.horizontalsystems.bitcoincore.apisync.blockchair.IBlockHashFetcher
4 | import io.horizontalsystems.bitcoincore.managers.ApiManager
5 |
6 | class HsBlockHashFetcher(url: String) : IBlockHashFetcher {
7 | private val apiManager = ApiManager(url)
8 |
9 | override fun fetch(heights: List): Map {
10 | val joinedHeights = heights.sorted().joinToString(",") { it.toString() }
11 | val blocks = apiManager.doOkHttpGet("hashes?numbers=$joinedHeights").asArray()
12 |
13 | return blocks.associate { blockJson ->
14 | val block = blockJson.asObject()
15 | Pair(
16 | block["number"].asInt(),
17 | block["hash"].asString()
18 | )
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/blockchair/BlockchairBlockHashFetcher.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.apisync.blockchair
2 |
3 |
4 | interface IBlockHashFetcher {
5 | fun fetch(heights: List): Map
6 | }
7 |
8 | class BlockchairBlockHashFetcher(
9 | private val blockchairApi: BlockchairApi
10 | ) : IBlockHashFetcher {
11 | override fun fetch(heights: List): Map {
12 | return blockchairApi.blockHashes(heights)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/blockchair/BlockchairLastBlockProvider.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.apisync.blockchair
2 |
3 | import io.horizontalsystems.bitcoincore.apisync.model.BlockHeaderItem
4 |
5 | class BlockchairLastBlockProvider(
6 | private val blockchairApi: BlockchairApi
7 | ) {
8 | fun lastBlockHeader(): BlockHeaderItem {
9 | return blockchairApi.lastBlockHeader()
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/blockchair/BlockchairTransactionProvider.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.apisync.blockchair
2 |
3 | import io.horizontalsystems.bitcoincore.apisync.model.TransactionItem
4 | import io.horizontalsystems.bitcoincore.core.IApiTransactionProvider
5 |
6 | class BlockchairTransactionProvider(
7 | val blockchairApi: BlockchairApi,
8 | private val blockHashFetcher: IBlockHashFetcher
9 | ) : IApiTransactionProvider {
10 |
11 | private fun fillBlockHashes(items: List): List {
12 | val hashesMap = blockHashFetcher.fetch(items.map { it.blockHeight }.distinct())
13 | return items.mapNotNull { item ->
14 | hashesMap[item.blockHeight]?.let {
15 | item.copy(blockHash = it)
16 | }
17 | }
18 | }
19 |
20 | override fun transactions(addresses: List, stopHeight: Int?): List {
21 | val items = blockchairApi.transactions(addresses, stopHeight)
22 | return fillBlockHashes(items)
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/legacy/BlockHashScanHelper.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.apisync.legacy
2 |
3 | import io.horizontalsystems.bitcoincore.apisync.model.AddressItem
4 |
5 | interface IBlockHashScanHelper {
6 | fun lastUsedIndex(addresses: List>, addressItems: List): Int
7 | }
8 |
9 | class BlockHashScanHelper : IBlockHashScanHelper {
10 |
11 | override fun lastUsedIndex(addresses: List>, addressItems: List): Int {
12 | val searchAddressStrings = addressItems.map { it.address }
13 | val searchScriptStrings = addressItems.map { it.script }
14 |
15 | for (i in addresses.size - 1 downTo 0) {
16 | addresses[i].forEach { address ->
17 | if (searchAddressStrings.contains(address) || searchScriptStrings.any { script -> script.contains(address) }) {
18 | return i
19 | }
20 | }
21 | }
22 |
23 | return -1
24 | }
25 |
26 | }
27 |
28 | class WatchAddressBlockHashScanHelper : IBlockHashScanHelper {
29 |
30 | override fun lastUsedIndex(addresses: List>, addressItems: List): Int = -1
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/legacy/PublicKeyFetcher.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.apisync.legacy
2 |
3 | import io.horizontalsystems.bitcoincore.core.AccountWallet
4 | import io.horizontalsystems.bitcoincore.core.Wallet
5 | import io.horizontalsystems.bitcoincore.core.WatchAccountWallet
6 | import io.horizontalsystems.bitcoincore.models.PublicKey
7 |
8 |
9 | interface IPublicKeyFetcher {
10 | fun publicKeys(indices: IntRange, external: Boolean): List
11 | }
12 |
13 | interface IMultiAccountPublicKeyFetcher {
14 | val currentAccount: Int
15 | fun increaseAccount()
16 | }
17 |
18 | class PublicKeyFetcher(private val accountWallet: AccountWallet) : IPublicKeyFetcher {
19 | override fun publicKeys(indices: IntRange, external: Boolean): List {
20 | return accountWallet.publicKeys(indices, external)
21 | }
22 | }
23 |
24 | class WatchPublicKeyFetcher(private val watchAccountWallet: WatchAccountWallet) : IPublicKeyFetcher {
25 | override fun publicKeys(indices: IntRange, external: Boolean): List {
26 | return watchAccountWallet.publicKeys(indices, external)
27 | }
28 | }
29 |
30 | class MultiAccountPublicKeyFetcher(private val wallet: Wallet) : IPublicKeyFetcher, IMultiAccountPublicKeyFetcher {
31 | override fun publicKeys(indices: IntRange, external: Boolean): List {
32 | return wallet.publicKeys(currentAccount, indices, external)
33 | }
34 |
35 | override var currentAccount: Int = 0
36 | private set
37 |
38 | @Synchronized
39 | override fun increaseAccount() {
40 | currentAccount++
41 | }
42 | }
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/model/AddressItem.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.apisync.model
2 |
3 | data class AddressItem(
4 | val script: String,
5 | val address: String?
6 | )
7 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/model/BlockHeaderItem.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.apisync.model
2 |
3 | data class BlockHeaderItem(
4 | val hash: ByteArray,
5 | val height: Int,
6 | val timestamp: Long
7 | )
8 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/model/TransactionItem.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.apisync.model
2 |
3 | data class TransactionItem(
4 | val blockHash: String,
5 | val blockHeight: Int,
6 | val addressItems: MutableList
7 | )
8 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/BlockMedianTimeHelper.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.blocks
2 |
3 | import io.horizontalsystems.bitcoincore.core.IStorage
4 | import io.horizontalsystems.bitcoincore.models.Block
5 |
6 | class BlockMedianTimeHelper(
7 | private val storage: IStorage,
8 | // This flag must be set ONLY when it's NOT possible to get needed blocks for median time calculation
9 | private val approximate: Boolean = false
10 | ) {
11 | private val medianTimeSpan = 11
12 |
13 | val medianTimePast: Long?
14 | get() =
15 | storage.lastBlock()?.let { lastBlock ->
16 | if (approximate) {
17 | // The median time is 6 blocks earlier which is approximately equal to 1 hour.
18 | lastBlock.timestamp - 3600
19 | } else {
20 | medianTimePast(lastBlock)
21 | }
22 | }
23 |
24 | fun medianTimePast(block: Block): Long? {
25 | val startIndex = block.height - medianTimeSpan + 1
26 | val median = storage.timestamps(from = startIndex, to = block.height)
27 |
28 | return when {
29 | block.height >= medianTimeSpan && median.size < medianTimeSpan -> null
30 | else -> median[median.size / 2]
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/BloomFilterLoader.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.blocks
2 |
3 | import io.horizontalsystems.bitcoincore.crypto.BloomFilter
4 | import io.horizontalsystems.bitcoincore.managers.BloomFilterManager
5 | import io.horizontalsystems.bitcoincore.network.peer.Peer
6 | import io.horizontalsystems.bitcoincore.network.peer.PeerGroup
7 | import io.horizontalsystems.bitcoincore.network.peer.PeerManager
8 |
9 | class BloomFilterLoader(private val bloomFilterManager: BloomFilterManager, private val peerManager: PeerManager)
10 | : PeerGroup.Listener, BloomFilterManager.Listener {
11 |
12 | override fun onPeerConnect(peer: Peer) {
13 | bloomFilterManager.bloomFilter?.let {
14 | peer.filterLoad(it)
15 | }
16 | }
17 |
18 | override fun onFilterUpdated(bloomFilter: BloomFilter) {
19 | peerManager.connected().forEach {
20 | it.filterLoad(bloomFilter)
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/IBlockchainDataListener.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.blocks
2 |
3 | import io.horizontalsystems.bitcoincore.models.Block
4 | import io.horizontalsystems.bitcoincore.models.Transaction
5 |
6 | interface IBlockchainDataListener {
7 | fun onBlockInsert(block: Block)
8 | fun onTransactionsUpdate(inserted: List, updated: List, block: Block?)
9 | fun onTransactionsDelete(hashes: List)
10 | }
11 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/IPeerSyncListener.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.blocks
2 |
3 | import io.horizontalsystems.bitcoincore.network.peer.Peer
4 |
5 | interface IPeerSyncListener {
6 | fun onAllPeersSynced() = Unit
7 | fun onPeerSynced(peer: Peer) = Unit
8 | }
9 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/MerkleBlockExtractor.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.blocks
2 |
3 | import io.horizontalsystems.bitcoincore.core.HashBytes
4 | import io.horizontalsystems.bitcoincore.models.MerkleBlock
5 | import io.horizontalsystems.bitcoincore.network.messages.MerkleBlockMessage
6 | import io.horizontalsystems.bitcoincore.utils.MerkleBranch
7 |
8 | class MerkleBlockExtractor(private val maxBlockSize: Int) {
9 |
10 | fun extract(message: MerkleBlockMessage): MerkleBlock {
11 | val matchedHashes = mutableMapOf()
12 | val merkleRoot = MerkleBranch().calculateMerkleRoot(message.txCount, message.hashes, message.flags, matchedHashes)
13 |
14 | message.apply {
15 | if (txCount < 1 || txCount > maxBlockSize / 60) {
16 | throw InvalidMerkleBlockException(String.format("Transaction count %d is not valid", txCount))
17 | }
18 |
19 | if (hashCount < 0 || hashCount > txCount) {
20 | throw InvalidMerkleBlockException(String.format("Hash count %d is not valid", hashCount))
21 | }
22 |
23 | if (flagsCount < 1) {
24 | throw InvalidMerkleBlockException(String.format("Flag count %d is not valid", flagsCount))
25 | }
26 |
27 | if (!header.merkleRoot.contentEquals(merkleRoot)) {
28 | throw InvalidMerkleBlockException("Merkle root is not valid")
29 | }
30 | }
31 |
32 | return MerkleBlock(message.header, matchedHashes)
33 | }
34 | }
35 |
36 | class InvalidMerkleBlockException(message: String) : Exception(message)
37 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/BitsValidator.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.blocks.validators
2 |
3 | import io.horizontalsystems.bitcoincore.models.Block
4 |
5 | class BitsValidator : IBlockChainedValidator {
6 |
7 | override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {
8 | return true
9 | }
10 |
11 | override fun validate(block: Block, previousBlock: Block) {
12 | if (block.bits != previousBlock.bits) {
13 | throw BlockValidatorException.NotEqualBits()
14 | }
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/BlockValidatorChain.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.blocks.validators
2 |
3 | import io.horizontalsystems.bitcoincore.models.Block
4 |
5 | class BlockValidatorChain : IBlockChainedValidator {
6 |
7 | private val concreteValidators = mutableListOf()
8 |
9 | override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {
10 | return true
11 | }
12 |
13 | override fun validate(block: Block, previousBlock: Block) {
14 | concreteValidators.firstOrNull { validator ->
15 | validator.isBlockValidatable(block, previousBlock)
16 | }?.validate(block, previousBlock)
17 | }
18 |
19 | fun add(validator: IBlockChainedValidator) {
20 | concreteValidators.add(validator)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/BlockValidatorException.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.blocks.validators
2 |
3 | import io.horizontalsystems.bitcoincore.extensions.toReversedHex
4 |
5 | open class BlockValidatorException(msg: String) : RuntimeException(msg) {
6 | class NoHeader : BlockValidatorException("No Header")
7 | class NoCheckpointBlock : BlockValidatorException("No Checkpoint Block")
8 | class NoPreviousBlock : BlockValidatorException("No PreviousBlock")
9 | class WrongPreviousHeader : BlockValidatorException("Wrong Previous Header Hash")
10 | class NotEqualBits : BlockValidatorException("Not Equal Bits")
11 | class NotDifficultyTransitionEqualBits : BlockValidatorException("Not Difficulty Transition Equal Bits")
12 | class InvalidProofOfWork : BlockValidatorException("Invalid Prove of Work")
13 | class WrongBlockHash(expected: ByteArray, actual: ByteArray) : BlockValidatorException("Wrong Block Hash ${actual.toReversedHex()} vs expected ${expected.toReversedHex()}")
14 | }
15 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/BlockValidatorSet.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.blocks.validators
2 |
3 | import io.horizontalsystems.bitcoincore.models.Block
4 |
5 | class BlockValidatorSet : IBlockValidator {
6 | private var validators = mutableListOf()
7 |
8 | override fun validate(block: Block, previousBlock: Block) {
9 | validators.forEach {
10 | it.validate(block, previousBlock)
11 | }
12 | }
13 |
14 | fun addBlockValidator(blockValidator: IBlockValidator) {
15 | validators.add(blockValidator)
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/IBlockValidator.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.blocks.validators
2 |
3 | import io.horizontalsystems.bitcoincore.models.Block
4 |
5 | interface IBlockValidator {
6 | fun validate(block: Block, previousBlock: Block)
7 | }
8 |
9 | interface IBlockChainedValidator : IBlockValidator {
10 | fun isBlockValidatable(block: Block, previousBlock: Block): Boolean
11 | }
12 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/LegacyTestNetDifficultyValidator.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.blocks.validators
2 |
3 | import io.horizontalsystems.bitcoincore.core.IStorage
4 | import io.horizontalsystems.bitcoincore.models.Block
5 |
6 | class LegacyTestNetDifficultyValidator(
7 | private val storage: IStorage,
8 | private val heightInterval: Long,
9 | private val targetSpacing: Int,
10 | private val maxTargetBits: Long)
11 | : IBlockChainedValidator {
12 |
13 | private val diffDate = 1329264000 // February 16th 2012
14 |
15 | override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {
16 | return previousBlock.timestamp > diffDate
17 | }
18 |
19 | override fun validate(block: Block, previousBlock: Block) {
20 | val timeDelta = block.timestamp - previousBlock.timestamp
21 | if (timeDelta >= 0 && timeDelta <= targetSpacing * 2) {
22 | var cursor = block
23 |
24 | while (cursor.height % heightInterval != 0L && cursor.bits == maxTargetBits) {
25 | val prevBlock = storage.getBlock(hashHash = cursor.previousBlockHash)
26 | ?: throw BlockValidatorException.NoPreviousBlock()
27 |
28 | cursor = prevBlock
29 | }
30 |
31 | if (cursor.bits != block.bits) {
32 | BlockValidatorException.NotEqualBits()
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/ProofOfWorkValidator.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.blocks.validators
2 |
3 | import io.horizontalsystems.bitcoincore.crypto.CompactBits
4 | import io.horizontalsystems.bitcoincore.extensions.toReversedHex
5 | import io.horizontalsystems.bitcoincore.models.Block
6 | import java.math.BigInteger
7 |
8 | class ProofOfWorkValidator : IBlockChainedValidator {
9 |
10 | override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {
11 | return true
12 | }
13 |
14 | override fun validate(block: Block, previousBlock: Block) {
15 | check(BigInteger(block.headerHash.toReversedHex(), 16) < CompactBits.decode(block.bits)) {
16 | throw BlockValidatorException.InvalidProofOfWork()
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/AccountWallet.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.core
2 |
3 | import io.horizontalsystems.bitcoincore.models.PublicKey
4 | import io.horizontalsystems.hdwalletkit.HDKey
5 | import io.horizontalsystems.hdwalletkit.HDWallet.*
6 | import io.horizontalsystems.hdwalletkit.HDWalletAccount
7 |
8 | class AccountWallet(private val hdWallet: HDWalletAccount, override val gapLimit: Int): IPrivateWallet, IAccountWallet {
9 |
10 | override fun publicKey(index: Int, external: Boolean): PublicKey {
11 | val pubKey = hdWallet.publicKey(index, if (external) Chain.EXTERNAL else Chain.INTERNAL)
12 | return PublicKey(0, index, external, pubKey.publicKey, pubKey.publicKeyHash)
13 | }
14 |
15 | override fun publicKeys(indices: IntRange, external: Boolean): List {
16 | val hdPublicKeys = hdWallet.publicKeys(indices, if (external) Chain.EXTERNAL else Chain.INTERNAL)
17 |
18 | if (hdPublicKeys.size != indices.count()) {
19 | throw Wallet.HDWalletError.PublicKeysDerivationFailed()
20 | }
21 |
22 | return indices.mapIndexed { position, index ->
23 | val hdPublicKey = hdPublicKeys[position]
24 | PublicKey(0, index, external, hdPublicKey.publicKey, hdPublicKey.publicKeyHash)
25 | }
26 | }
27 |
28 | override fun privateKey(account: Int, index: Int, external: Boolean): HDKey {
29 | return hdWallet.privateKey(index, if (external) Chain.EXTERNAL else Chain.INTERNAL)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/DoubleSha256Hasher.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.core
2 |
3 | import io.horizontalsystems.bitcoincore.utils.HashUtils
4 |
5 | class DoubleSha256Hasher : IHasher {
6 | override fun hash(data: ByteArray): ByteArray {
7 | return HashUtils.doubleSha256(data)
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/HashBytes.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.core
2 |
3 | import io.horizontalsystems.bitcoincore.extensions.toHexString
4 | import io.horizontalsystems.bitcoincore.utils.Utils
5 |
6 | /**
7 | * Just wraps a ByteArray so that equals and hashcode work correctly, allowing it to be
8 | * used as keys in a map
9 | */
10 | class HashBytes(val bytes: ByteArray) : Comparable {
11 | private val hashLength = 32
12 |
13 | override fun equals(other: Any?): Boolean {
14 | if (this === other) return true
15 | if (other == null || other !is HashBytes) return false
16 |
17 | return bytes.contentEquals(other.bytes)
18 | }
19 |
20 | override fun hashCode(): Int {
21 | // Use the first 4 bytes, not the last 4 which are often zeros (in little-endian)
22 | return Utils.intFromBytes(bytes[0], bytes[1], bytes[2], bytes[3])
23 | }
24 |
25 | override fun compareTo(other: HashBytes): Int {
26 | for (i in 0 until hashLength) {
27 | val b1 = bytes[i].toInt() and 0xff
28 | val b2 = other.bytes[i].toInt() and 0xff
29 |
30 | val res = b1.compareTo(b2)
31 | if (res != 0) return res
32 | }
33 |
34 | return 0
35 | }
36 |
37 | override fun toString(): String {
38 | return bytes.toHexString()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/IHasher.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.core
2 |
3 | interface IHasher {
4 | fun hash(data: ByteArray) : ByteArray
5 | }
6 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/TransactionDataSorterFactory.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.core
2 |
3 | import io.horizontalsystems.bitcoincore.models.TransactionDataSortType
4 | import io.horizontalsystems.bitcoincore.utils.Bip69Sorter
5 | import io.horizontalsystems.bitcoincore.utils.ShuffleSorter
6 | import io.horizontalsystems.bitcoincore.utils.StraightSorter
7 |
8 | class TransactionDataSorterFactory : ITransactionDataSorterFactory {
9 | override fun sorter(type: TransactionDataSortType): ITransactionDataSorter {
10 | return when (type) {
11 | TransactionDataSortType.None -> StraightSorter()
12 | TransactionDataSortType.Shuffle -> ShuffleSorter()
13 | TransactionDataSortType.Bip69 -> Bip69Sorter()
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/TransactionInfoConverter.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.core
2 |
3 | import io.horizontalsystems.bitcoincore.models.TransactionInfo
4 | import io.horizontalsystems.bitcoincore.storage.FullTransactionInfo
5 |
6 | class TransactionInfoConverter : ITransactionInfoConverter {
7 | override lateinit var baseConverter: BaseTransactionInfoConverter
8 |
9 | override fun transactionInfo(fullTransactionInfo: FullTransactionInfo): TransactionInfo {
10 | return baseConverter.transactionInfo(fullTransactionInfo)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/Wallet.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.core
2 |
3 | import io.horizontalsystems.bitcoincore.models.PublicKey
4 | import io.horizontalsystems.hdwalletkit.HDKey
5 | import io.horizontalsystems.hdwalletkit.HDWallet
6 | import java.lang.Exception
7 |
8 | class Wallet(private val hdWallet: HDWallet, val gapLimit: Int): IPrivateWallet {
9 |
10 | fun publicKey(account: Int, index: Int, external: Boolean): PublicKey {
11 | val hdPubKey = hdWallet.hdPublicKey(account, index, external)
12 | return PublicKey(account, index, external, hdPubKey.publicKey, hdPubKey.publicKeyHash)
13 | }
14 |
15 | fun publicKeys(account: Int, indices: IntRange, external: Boolean): List {
16 | val hdPublicKeys = hdWallet.hdPublicKeys(account, indices, external)
17 |
18 | if (hdPublicKeys.size != indices.count()) {
19 | throw HDWalletError.PublicKeysDerivationFailed()
20 | }
21 |
22 | return indices.mapIndexed { position, index ->
23 | val hdPublicKey = hdPublicKeys[position]
24 | PublicKey(account, index, external, hdPublicKey.publicKey, hdPublicKey.publicKeyHash)
25 | }
26 | }
27 |
28 | override fun privateKey(account: Int, index: Int, external: Boolean): HDKey {
29 | return hdWallet.privateKey(account, index, external)
30 | }
31 |
32 | open class HDWalletError : Exception() {
33 | class PublicKeysDerivationFailed : HDWalletError()
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/WatchAccountWallet.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.core
2 |
3 | import io.horizontalsystems.bitcoincore.models.PublicKey
4 | import io.horizontalsystems.hdwalletkit.HDWallet
5 | import io.horizontalsystems.hdwalletkit.HDWalletAccountWatch
6 |
7 | class WatchAccountWallet(private val hdWallet: HDWalletAccountWatch, override val gapLimit: Int): IAccountWallet {
8 |
9 | override fun publicKey(index: Int, external: Boolean): PublicKey {
10 | val pubKey = hdWallet.publicKey(index, if (external) HDWallet.Chain.EXTERNAL else HDWallet.Chain.INTERNAL)
11 | return PublicKey(0, index, external, pubKey.publicKey, pubKey.publicKeyHash)
12 | }
13 |
14 | override fun publicKeys(indices: IntRange, external: Boolean): List {
15 | val hdPublicKeys = hdWallet.publicKeys(indices, if (external) HDWallet.Chain.EXTERNAL else HDWallet.Chain.INTERNAL)
16 |
17 | if (hdPublicKeys.size != indices.count()) {
18 | throw Wallet.HDWalletError.PublicKeysDerivationFailed()
19 | }
20 |
21 | return indices.mapIndexed { position, index ->
22 | val hdPublicKey = hdPublicKeys[position]
23 | PublicKey(0, index, external, hdPublicKey.publicKey, hdPublicKey.publicKeyHash)
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/extensions/Array.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.extensions
2 |
3 | fun ByteArray.toHexString(): String {
4 | return this.joinToString(separator = "") {
5 | it.toInt().and(0xff).toString(16).padStart(2, '0')
6 | }
7 | }
8 |
9 | fun ByteArray.toReversedHex(): String {
10 | return reversedArray().toHexString()
11 | }
12 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/extensions/String.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.extensions
2 |
3 | fun String.hexToByteArray(): ByteArray {
4 | return ByteArray(this.length / 2) {
5 | this.substring(it * 2, it * 2 + 2).toInt(16).toByte()
6 | }
7 | }
8 |
9 | fun String.toReversedByteArray(): ByteArray {
10 | return hexToByteArray().reversedArray()
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/ApiSyncStateManager.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.managers
2 |
3 | import io.horizontalsystems.bitcoincore.core.IStorage
4 |
5 | class ApiSyncStateManager(
6 | private val storage: IStorage,
7 | private val restoreFromApi: Boolean
8 | ) {
9 |
10 | var restored: Boolean
11 | get() {
12 | if (!restoreFromApi) {
13 | return true
14 | }
15 |
16 | return storage.initialRestored ?: false
17 | }
18 | set(value) {
19 | storage.setInitialRestored(value)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/BlockValidatorHelper.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.managers
2 |
3 | import io.horizontalsystems.bitcoincore.core.IStorage
4 | import io.horizontalsystems.bitcoincore.models.Block
5 |
6 | open class BlockValidatorHelper(protected val storage: IStorage) {
7 | fun getPreviousChunk(blockHeight: Int, size: Int): List {
8 | return storage.getBlocksChunk(fromHeight = blockHeight - size, toHeight = blockHeight)
9 | }
10 |
11 | /**
12 | * NOTE: When there is a fork there may be 2 blocks with the same height.
13 | *
14 | * In this case we need to retrieve block that is related to the current syncing peer.
15 | * The blocks of current syncing peer are stored in a database with "stale" set to true.
16 | * So if there is a 2 blocks with the same height we need to retrieve one with "stale" = true.
17 | *
18 | * Prioritizing stale blocks resolves it:
19 | * - if there are 2 blocks then it will retrieve one with "stale" = true
20 | * - if there is only 1 block then it will retrieve it. No matter what is set for "stale"
21 | * - if there is no block for the given height then it will return null
22 | */
23 | fun getPrevious(block: Block, stepBack: Int): Block? {
24 | return storage.getBlockByHeightStalePrioritized(block.height - stepBack)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/BloomFilterManager.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.managers
2 |
3 | import io.horizontalsystems.bitcoincore.crypto.BloomFilter
4 |
5 | class BloomFilterManager {
6 |
7 | object BloomFilterExpired : Exception()
8 |
9 | interface Listener {
10 | fun onFilterUpdated(bloomFilter: BloomFilter)
11 | }
12 |
13 | var listener: Listener? = null
14 | var bloomFilter: BloomFilter? = null
15 |
16 | private val bloomFilterProviders = mutableListOf()
17 |
18 | init {
19 | regenerateBloomFilter()
20 | }
21 |
22 | fun regenerateBloomFilter() {
23 | val elements = mutableListOf()
24 |
25 | bloomFilterProviders.forEach {
26 | elements.addAll(it.getBloomFilterElements())
27 | }
28 |
29 | if (elements.isNotEmpty()) {
30 | BloomFilter(elements).let {
31 | bloomFilter = it
32 | listener?.onFilterUpdated(it)
33 | }
34 | }
35 | }
36 |
37 | fun addBloomFilterProvider(provider: IBloomFilterProvider) {
38 | provider.bloomFilterManager = this
39 | bloomFilterProviders.add(provider)
40 | }
41 | }
42 |
43 | interface IBloomFilterProvider {
44 | var bloomFilterManager: BloomFilterManager?
45 |
46 | fun getBloomFilterElements(): List
47 | }
48 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/IUnspentOutputProvider.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.managers
2 |
3 | import io.horizontalsystems.bitcoincore.storage.UnspentOutput
4 | import io.horizontalsystems.bitcoincore.storage.UtxoFilters
5 |
6 | interface IUnspentOutputProvider {
7 | fun getSpendableUtxo(filters: UtxoFilters): List
8 | }
9 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/PendingOutpointsProvider.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.managers
2 |
3 | import io.horizontalsystems.bitcoincore.core.IStorage
4 | import io.horizontalsystems.bitcoincore.utils.Utils
5 |
6 | class PendingOutpointsProvider(private val storage: IStorage) : IBloomFilterProvider {
7 |
8 | override var bloomFilterManager: BloomFilterManager? = null
9 |
10 | override fun getBloomFilterElements(): List {
11 | val incomingPendingTxHashes = storage.getIncomingPendingTxHashes()
12 | val inputs = storage.getTransactionInputs(incomingPendingTxHashes)
13 |
14 | return inputs.map { input -> input.previousOutputTxHash + Utils.intToByteArray(input.previousOutputIndex.toInt()).reversedArray() }
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/BitcoinPaymentData.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.models
2 |
3 | data class BitcoinPaymentData(
4 | val address: String,
5 | val version: String? = null,
6 | val amount: Double? = null,
7 | val label: String? = null,
8 | val message: String? = null,
9 | val parameters: MutableMap? = null) {
10 |
11 | val uriPaymentAddress: String
12 | get() {
13 | val uriAddress = address
14 | version?.let {
15 | uriAddress.plus(";version=$version")
16 | }
17 | amount?.let {
18 | uriAddress.plus("?amount=$it")
19 | }
20 | label?.let {
21 | uriAddress.plus("?label=$label")
22 | }
23 | message?.let {
24 | uriAddress.plus("?message=$message")
25 | }
26 | parameters?.let {
27 | for ((name, value) in it) {
28 | uriAddress.plus("?$name=$value")
29 | }
30 | }
31 |
32 | return uriAddress
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/BitcoinSendInfo.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.models
2 |
3 | import io.horizontalsystems.bitcoincore.storage.UnspentOutput
4 |
5 | data class BitcoinSendInfo(
6 | val unspentOutputs: List,
7 | val fee: Long,
8 | val changeValue: Long?,
9 | val changeAddress: Address?
10 | )
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/BlockHash.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.models
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity
7 | class BlockHash(
8 | @PrimaryKey
9 | val headerHash: ByteArray,
10 | val height: Int,
11 | val sequence: Int = 0)
12 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/BlockHashPublicKey.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.models
2 |
3 | import androidx.room.Entity
4 | import androidx.room.ForeignKey
5 |
6 | @Entity(
7 | primaryKeys = ["blockHash", "publicKeyPath"],
8 | foreignKeys = [
9 | ForeignKey(
10 | entity = BlockHash::class,
11 | parentColumns = ["headerHash"],
12 | childColumns = ["blockHash"],
13 | onUpdate = ForeignKey.CASCADE,
14 | onDelete = ForeignKey.CASCADE,
15 | deferred = true
16 | ),
17 | ForeignKey(
18 | entity = PublicKey::class,
19 | parentColumns = ["path"],
20 | childColumns = ["publicKeyPath"],
21 | onUpdate = ForeignKey.CASCADE,
22 | onDelete = ForeignKey.CASCADE,
23 | deferred = true
24 | )
25 | ]
26 | )
27 | class BlockHashPublicKey(
28 | val blockHash: ByteArray,
29 | val publicKeyPath: String
30 | )
31 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/BlockchainState.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.models
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity
7 | data class BlockchainState(var initialRestored: Boolean?) {
8 |
9 | @PrimaryKey
10 | var primaryKey: String = "primary-key"
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/InvalidTransaction.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.models
2 |
3 | import androidx.room.Entity
4 |
5 | @Entity
6 | class InvalidTransaction() : Transaction() {
7 |
8 | constructor(transaction: Transaction, serializedTxInfo: String, rawTransaction: String?) : this() {
9 | uid = transaction.uid
10 | hash = transaction.hash
11 | blockHash = transaction.blockHash
12 | version = transaction.version
13 | lockTime = transaction.lockTime
14 | timestamp = transaction.timestamp
15 | order = transaction.order
16 | isMine = transaction.isMine
17 | isOutgoing = transaction.isOutgoing
18 | segwit = transaction.segwit
19 | status = Status.INVALID
20 | conflictingTxHash = transaction.conflictingTxHash
21 |
22 | this.serializedTxInfo = serializedTxInfo
23 | this.rawTransaction = rawTransaction
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/MerkleBlock.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.models
2 |
3 | import io.horizontalsystems.bitcoincore.core.HashBytes
4 | import io.horizontalsystems.bitcoincore.storage.BlockHeader
5 | import io.horizontalsystems.bitcoincore.storage.FullTransaction
6 |
7 | class MerkleBlock(val header: BlockHeader, val associatedTransactionHashes: Map) {
8 |
9 | var height: Int? = null
10 | var associatedTransactions = mutableListOf()
11 | val blockHash = header.hash
12 |
13 | val complete: Boolean
14 | get() = associatedTransactionHashes.size == associatedTransactions.size
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/PeerAddress.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.models
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity
7 | data class PeerAddress(@PrimaryKey var ip: String, var score: Int = 0, var connectionTime: Long? = null)
8 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/SentTransaction.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.models
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity
7 | class SentTransaction() {
8 |
9 | @PrimaryKey
10 | var hash = byteArrayOf()
11 | var firstSendTime: Long = System.currentTimeMillis()
12 | var lastSendTime: Long = System.currentTimeMillis()
13 | var retriesCount: Int = 0
14 | var sendSuccess: Boolean = false
15 |
16 | constructor(hash: ByteArray) : this() {
17 | this.hash = hash
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/TransactionDataSortType.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.models
2 |
3 | enum class TransactionDataSortType {
4 | None, Shuffle, Bip69
5 | }
6 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/TransactionMetadata.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.models
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 | import androidx.room.TypeConverter
6 |
7 | @Entity
8 | class TransactionMetadata(
9 | @PrimaryKey
10 | val transactionHash: ByteArray
11 | ) {
12 | var amount: Long = 0
13 | var type: TransactionType = TransactionType.Incoming
14 | var fee: Long? = null
15 | }
16 |
17 | enum class TransactionType(val value: Int) {
18 | Incoming(1),
19 | Outgoing(2),
20 | SentToSelf(3);
21 |
22 | companion object {
23 | fun fromValue(value: Int) = values().find { it.value == value }
24 | }
25 | }
26 |
27 | enum class TransactionFilterType(val types: List) {
28 | Incoming(listOf(TransactionType.Incoming, TransactionType.SentToSelf)),
29 | Outgoing(listOf(TransactionType.Outgoing, TransactionType.SentToSelf))
30 | }
31 |
32 | class TransactionTypeConverter {
33 | @TypeConverter
34 | fun fromInt(value: Int?): TransactionType? {
35 | return value?.let { TransactionType.fromValue(it) }
36 | }
37 |
38 | @TypeConverter
39 | fun transactionTypeToInt(transactionTypeToInt: TransactionType?): Int? {
40 | return transactionTypeToInt?.value
41 | }
42 | }
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/UsedAddress.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.models
2 |
3 | data class UsedAddress(
4 | val index: Int,
5 | val address: String
6 | )
7 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/WatchAddressPublicKey.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.models
2 |
3 | import io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType
4 |
5 | class WatchAddressPublicKey(
6 | data: ByteArray,
7 | scriptType: ScriptType
8 | ) : PublicKey() {
9 | init {
10 | path = "WatchAddressPublicKey"
11 | when (scriptType) {
12 | ScriptType.P2PKH,
13 | ScriptType.P2WPKH -> {
14 | publicKeyHash = data
15 | }
16 |
17 | ScriptType.P2SH,
18 | ScriptType.P2WSH,
19 | ScriptType.P2WPKHSH -> {
20 | scriptHashP2WPKH = data
21 | }
22 |
23 | ScriptType.P2TR -> {
24 | convertedForP2TR = data
25 | }
26 |
27 | ScriptType.P2PK -> {
28 | publicKey = data
29 | }
30 |
31 | ScriptType.NULL_DATA,
32 | ScriptType.UNKNOWN -> {
33 | Unit // Not supported yet
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/Network.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network
2 |
3 | import io.horizontalsystems.bitcoincore.models.Checkpoint
4 | import io.horizontalsystems.bitcoincore.transactions.scripts.Sighash
5 | import io.horizontalsystems.bitcoincore.utils.HashUtils
6 |
7 | abstract class Network {
8 |
9 | open val protocolVersion = 70014
10 | open val syncableFromApi = true
11 | val bloomFilterVersion = 70000
12 | open val noBloomVersion = 70011
13 | val networkServices = 0L
14 | val serviceFullNode = 1L
15 | val serviceBloomFilter = 4L
16 | val zeroHashBytes = HashUtils.toBytesAsLE("0000000000000000000000000000000000000000000000000000000000000000")
17 |
18 | abstract val blockchairChainId: String
19 |
20 | abstract val maxBlockSize: Int
21 | abstract val dustRelayTxFee: Int
22 |
23 | abstract var port: Int
24 | abstract var magic: Long
25 | abstract var bip32HeaderPub: Int
26 | abstract var bip32HeaderPriv: Int
27 | abstract var coinType: Int
28 | abstract var dnsSeeds: List
29 | abstract var addressVersion: Int
30 | abstract var addressSegwitHrp: String
31 | abstract var addressScriptVersion: Int
32 |
33 | open val bip44Checkpoint = Checkpoint("${javaClass.simpleName}-bip44.checkpoint")
34 | open val lastCheckpoint = Checkpoint("${javaClass.simpleName}.checkpoint")
35 |
36 | open val sigHashForked: Boolean = false
37 | open val sigHashValue = Sighash.ALL
38 | }
39 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/AddrMessage.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.messages
2 |
3 | import io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable
4 | import io.horizontalsystems.bitcoincore.models.NetworkAddress
5 |
6 | class AddrMessage(var addresses: List) : IMessage {
7 | override fun toString(): String {
8 | return "AddrMessage(count=${addresses.size})"
9 | }
10 | }
11 |
12 | class AddrMessageParser : IMessageParser {
13 | override val command = "addr"
14 |
15 | override fun parseMessage(input: BitcoinInputMarkable): IMessage {
16 | val count = input.readVarInt() // do not store count
17 |
18 | val addresses = List(count.toInt()) {
19 | NetworkAddress(input, false)
20 | }
21 |
22 | return AddrMessage(addresses)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/FilterLoadMessage.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.messages
2 |
3 | import io.horizontalsystems.bitcoincore.crypto.BloomFilter
4 |
5 | class FilterLoadMessage(bloomFilter: BloomFilter) : IMessage {
6 | var filter: BloomFilter = bloomFilter
7 |
8 | override fun toString(): String {
9 | return "FilterLoadMessage($filter)"
10 | }
11 | }
12 |
13 | class FilterLoadMessageSerializer : IMessageSerializer {
14 | override val command: String = "filterload"
15 |
16 | override fun serialize(message: IMessage): ByteArray? {
17 | if (message !is FilterLoadMessage) {
18 | return null
19 | }
20 |
21 | return message.filter.toByteArray()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/GetBlocksMessage.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.messages
2 |
3 | import io.horizontalsystems.bitcoincore.extensions.toReversedHex
4 | import io.horizontalsystems.bitcoincore.io.BitcoinOutput
5 |
6 | class GetBlocksMessage(var hashes: List, var version: Int, var hashStop: ByteArray) : IMessage {
7 | override fun toString(): String {
8 | val list = hashes
9 | .take(10)
10 | .map { hash -> hash.toReversedHex() }
11 | .joinToString()
12 |
13 | return ("GetBlocksMessage(" + hashes.size + ": [" + list + "], hashStop=" + hashStop.toReversedHex() + ")")
14 | }
15 | }
16 |
17 | class GetBlocksMessageSerializer : IMessageSerializer {
18 | override val command: String = "getblocks"
19 |
20 | override fun serialize(message: IMessage): ByteArray? {
21 | if (message !is GetBlocksMessage) {
22 | return null
23 | }
24 |
25 | val output = BitcoinOutput()
26 | output.writeInt(message.version).writeVarInt(message.hashes.size.toLong())
27 | message.hashes.forEach {
28 | output.write(it)
29 | }
30 | output.write(message.hashStop)
31 | return output.toByteArray()
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/GetHeadersMessage.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.messages
2 |
3 | import io.horizontalsystems.bitcoincore.extensions.toReversedHex
4 | import io.horizontalsystems.bitcoincore.io.BitcoinOutput
5 |
6 | class GetHeadersMessage(var version: Int, var hashes: List, var hashStop: ByteArray) : IMessage {
7 | override fun toString(): String {
8 | return ("GetHeadersMessage(${hashes.size}: hashStop=${hashStop.toReversedHex()})")
9 | }
10 | }
11 |
12 | class GetHeadersMessageSerializer : IMessageSerializer {
13 | override val command: String = "getheaders"
14 |
15 | override fun serialize(message: IMessage): ByteArray? {
16 | if (message !is GetHeadersMessage) {
17 | return null
18 | }
19 |
20 | val output = BitcoinOutput().also {
21 | it.writeInt(message.version)
22 | it.writeVarInt(message.hashes.size.toLong())
23 | }
24 |
25 | message.hashes.forEach {
26 | output.write(it)
27 | }
28 |
29 | output.write(message.hashStop)
30 |
31 | return output.toByteArray()
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/MempoolMessage.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.messages
2 |
3 | class MempoolMessage : IMessage {
4 | override fun toString(): String {
5 | return "MempoolMessage()"
6 | }
7 | }
8 |
9 | class MempoolMessageSerializer : IMessageSerializer {
10 | override val command: String = "mempool"
11 |
12 | override fun serialize(message: IMessage): ByteArray? {
13 | if (message !is MempoolMessage) {
14 | return null
15 | }
16 |
17 | return ByteArray(0)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/PingMessage.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.messages
2 |
3 | import io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable
4 | import io.horizontalsystems.bitcoincore.io.BitcoinOutput
5 |
6 | class PingMessage(val nonce: Long) : IMessage {
7 | override fun toString(): String {
8 | return "PingMessage(nonce=$nonce)"
9 | }
10 | }
11 |
12 | class PingMessageParser : IMessageParser {
13 | override val command: String = "ping"
14 |
15 | override fun parseMessage(input: BitcoinInputMarkable): IMessage {
16 | return PingMessage(input.readLong())
17 | }
18 | }
19 |
20 | class PingMessageSerializer : IMessageSerializer {
21 | override val command: String = "ping"
22 |
23 | override fun serialize(message: IMessage): ByteArray? {
24 | if (message !is PingMessage) {
25 | return null
26 | }
27 |
28 | return BitcoinOutput()
29 | .writeLong(message.nonce)
30 | .toByteArray()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/PongMessage.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.messages
2 |
3 | import io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable
4 | import io.horizontalsystems.bitcoincore.io.BitcoinOutput
5 |
6 | class PongMessage(val nonce: Long) : IMessage {
7 | override fun toString(): String {
8 | return "PongMessage(nonce=$nonce)"
9 | }
10 | }
11 |
12 | class PongMessageParser : IMessageParser {
13 | override val command: String = "pong"
14 |
15 | override fun parseMessage(input: BitcoinInputMarkable): IMessage {
16 | return PongMessage(input.readLong())
17 | }
18 | }
19 |
20 | class PongMessageSerializer : IMessageSerializer {
21 | override val command: String = "pong"
22 |
23 | override fun serialize(message: IMessage): ByteArray? {
24 | if (message !is PongMessage) {
25 | return null
26 | }
27 |
28 | return BitcoinOutput()
29 | .writeLong(message.nonce)
30 | .toByteArray()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/RejectMessage.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.messages
2 |
3 | import io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable
4 |
5 | class RejectMessage(private val responseToMessage: String,
6 | private val rejectCode: Byte,
7 | private val reason: String) : IMessage {
8 |
9 | override fun toString(): String {
10 | return "RejectMessage(responseToMessage=$responseToMessage, rejectCode: $rejectCode, reason: $reason)"
11 | }
12 | }
13 |
14 | class RejectMessageParser : IMessageParser {
15 | override val command = "reject"
16 |
17 | override fun parseMessage(input: BitcoinInputMarkable): IMessage {
18 | val responseToMessage = input.readString()
19 | val rejectCode = input.readByte()
20 | val reason = input.readString()
21 |
22 | return RejectMessage(responseToMessage, rejectCode, reason)
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/TransactionMessage.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.messages
2 |
3 | import io.horizontalsystems.bitcoincore.extensions.toReversedHex
4 | import io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable
5 | import io.horizontalsystems.bitcoincore.serializers.TransactionSerializer
6 | import io.horizontalsystems.bitcoincore.storage.FullTransaction
7 |
8 | class TransactionMessage(var transaction: FullTransaction, val size: Int) : IMessage {
9 | override fun toString(): String {
10 | return "TransactionMessage(${transaction.header.hash.toReversedHex()})"
11 | }
12 | }
13 |
14 | class TransactionMessageParser : IMessageParser {
15 | override val command: String = "tx"
16 |
17 | override fun parseMessage(input: BitcoinInputMarkable): IMessage {
18 | val transaction = TransactionSerializer.deserialize(input)
19 | return TransactionMessage(transaction, input.count)
20 | }
21 | }
22 |
23 | class TransactionMessageSerializer : IMessageSerializer {
24 | override val command: String = "tx"
25 |
26 | override fun serialize(message: IMessage): ByteArray? {
27 | if (message !is TransactionMessage) {
28 | return null
29 | }
30 |
31 | return TransactionSerializer.serialize(message.transaction)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/UnknownMessage.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.messages
2 |
3 | class UnknownMessage(val command: String) : IMessage {
4 | override fun toString(): String {
5 | return "UnknownMessage(command=$command)"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/VerAckMessage.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.messages
2 |
3 | import io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable
4 |
5 | class VerAckMessage : IMessage {
6 | override fun toString(): String {
7 | return "VerAckMessage()"
8 | }
9 | }
10 |
11 | class VerAckMessageParser : IMessageParser {
12 | override val command: String = "verack"
13 |
14 | override fun parseMessage(input: BitcoinInputMarkable): IMessage {
15 | return VerAckMessage()
16 | }
17 | }
18 |
19 | class VerAckMessageSerializer : IMessageSerializer {
20 | override val command: String = "verack"
21 |
22 | override fun serialize(message: IMessage): ByteArray? {
23 | if (message !is VerAckMessage) {
24 | return null
25 | }
26 |
27 | return ByteArray(0)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/IInventoryItemsHandler.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.peer
2 |
3 | import io.horizontalsystems.bitcoincore.models.InventoryItem
4 |
5 | interface IInventoryItemsHandler {
6 | fun handleInventoryItems(peer: Peer, inventoryItems: List)
7 | }
8 |
9 | class InventoryItemsHandlerChain : IInventoryItemsHandler {
10 |
11 | private val concreteHandlers = mutableListOf()
12 |
13 | override fun handleInventoryItems(peer: Peer, inventoryItems: List) {
14 | concreteHandlers.forEach {
15 | it.handleInventoryItems(peer, inventoryItems)
16 | }
17 | }
18 |
19 | fun addHandler(h: IInventoryItemsHandler) {
20 | concreteHandlers.add(h)
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/IPeerTaskHandler.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.peer
2 |
3 | import io.horizontalsystems.bitcoincore.network.peer.task.PeerTask
4 |
5 | interface IPeerTaskHandler {
6 | fun handleCompletedTask(peer: Peer, task: PeerTask): Boolean
7 | }
8 |
9 | class PeerTaskHandlerChain : IPeerTaskHandler {
10 |
11 | private val concreteHandlers = mutableListOf()
12 |
13 | override fun handleCompletedTask(peer: Peer, task: PeerTask): Boolean {
14 | return concreteHandlers.any {
15 | it.handleCompletedTask(peer, task)
16 | }
17 | }
18 |
19 | fun addHandler(h: IPeerTaskHandler) {
20 | concreteHandlers.add(h)
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/PeerDiscover.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.peer
2 |
3 | import io.horizontalsystems.bitcoincore.core.IPeerAddressManager
4 | import kotlinx.coroutines.GlobalScope
5 | import kotlinx.coroutines.launch
6 | import java.net.Inet6Address
7 | import java.net.InetAddress
8 | import java.net.UnknownHostException
9 | import java.util.logging.Logger
10 |
11 | class PeerDiscover(private val peerAddressManager: IPeerAddressManager) {
12 |
13 | private val logger = Logger.getLogger("PeerDiscover")
14 |
15 | fun lookup(dnsList: List) {
16 | logger.info("Lookup peers from DNS seed...")
17 |
18 | // todo: launch coroutines for each dns resolve
19 | GlobalScope.launch {
20 | dnsList.forEach { host ->
21 | try {
22 | val ips = InetAddress
23 | .getAllByName(host)
24 | .filter { it !is Inet6Address }
25 | .map { it.hostAddress }
26 |
27 | logger.info("Fetched ${ips.size} peer addresses from host: $host")
28 | peerAddressManager.addIps(ips)
29 | } catch (e: UnknownHostException) {
30 | logger.warning("Cannot look up host: $host")
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/PeerManager.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.peer
2 |
3 | import java.util.concurrent.ConcurrentHashMap
4 |
5 | class PeerManager {
6 |
7 | private var peers = ConcurrentHashMap()
8 |
9 | val peersCount: Int
10 | get() = peers.size
11 |
12 | fun add(peer: Peer) {
13 | peers[peer.host] = peer
14 | }
15 |
16 | fun remove(peer: Peer) {
17 | peers.remove(peer.host)
18 | }
19 |
20 | fun disconnectAll() {
21 | peers.values.forEach { it.close() }
22 | peers.clear()
23 | }
24 |
25 | fun connected(): List {
26 | return peers.values.filter { it.connected }
27 | }
28 |
29 | fun sorted(): List {
30 | return connected().sortedBy { it.connectionTime }
31 | }
32 |
33 | fun readyPears(): List {
34 | return peers.values.filter { it.connected && it.ready }
35 | }
36 |
37 | fun hasSyncedPeer(): Boolean {
38 | return peers.values.any { it.connected && it.synced }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/PeerTimer.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.peer
2 |
3 | import java.util.concurrent.TimeUnit
4 |
5 | class PeerTimer {
6 |
7 | private val maxIdleTime = TimeUnit.SECONDS.toMillis(60)
8 | private val pingTimeout = TimeUnit.SECONDS.toMillis(5)
9 |
10 | private var messageLastReceivedTime: Long? = null
11 | private var lastPingTime: Long? = null
12 |
13 | fun check() {
14 | lastPingTime?.let {
15 | if (System.currentTimeMillis() - it > pingTimeout) {
16 | throw Error.Timeout(pingTimeout)
17 | }
18 | }
19 |
20 | messageLastReceivedTime?.let {
21 | if (lastPingTime == null && System.currentTimeMillis() - it > maxIdleTime) {
22 | throw Error.Idle()
23 | }
24 | }
25 | }
26 |
27 | fun pingSent() {
28 | lastPingTime = System.currentTimeMillis()
29 | }
30 |
31 | fun restart() {
32 | messageLastReceivedTime = System.currentTimeMillis()
33 | lastPingTime = null
34 | }
35 |
36 | open class Error(message: String) : Exception(message) {
37 | class Idle: Error("Idle")
38 | class Timeout(time: Long) : Error("No response within $time milliseconds")
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/task/GetBlockHeadersTask.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.peer.task
2 |
3 | import io.horizontalsystems.bitcoincore.network.messages.GetHeadersMessage
4 | import io.horizontalsystems.bitcoincore.network.messages.HeadersMessage
5 | import io.horizontalsystems.bitcoincore.network.messages.IMessage
6 | import io.horizontalsystems.bitcoincore.storage.BlockHeader
7 |
8 | class GetBlockHeadersTask(private val blockLocatorHashes: List) : PeerTask() {
9 |
10 | var blockHeaders = arrayOf()
11 |
12 | override fun start() {
13 | requester?.let { it.send(GetHeadersMessage(it.protocolVersion, blockLocatorHashes, ByteArray(32))) }
14 | resetTimer()
15 | }
16 |
17 | override fun handleMessage(message: IMessage): Boolean {
18 | if (message !is HeadersMessage) {
19 | return false
20 | }
21 |
22 | blockHeaders = message.headers
23 | listener?.onTaskCompleted(this)
24 |
25 | return true
26 | }
27 |
28 | override fun handleTimeout() {
29 | listener?.onTaskCompleted(this)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/task/PeerTask.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.peer.task
2 |
3 | import io.horizontalsystems.bitcoincore.network.messages.IMessage
4 |
5 | open class PeerTask {
6 |
7 | interface Listener {
8 | fun onTaskCompleted(task: PeerTask)
9 | fun onTaskFailed(task: PeerTask, e: Exception)
10 | }
11 |
12 | interface Requester {
13 | val protocolVersion: Int
14 | fun send(message: IMessage)
15 | }
16 |
17 | var requester: Requester? = null
18 | var listener: Listener? = null
19 | open val state: String = ""
20 |
21 | protected var lastActiveTime: Long? = null
22 | protected var allowedIdleTime: Long? = null
23 |
24 | open fun start() = Unit
25 | open fun handleTimeout() = Unit
26 |
27 | open fun checkTimeout() {
28 | allowedIdleTime?.let { allowedIdleTime ->
29 | lastActiveTime?.let { lastActiveTime ->
30 | if (System.currentTimeMillis() - lastActiveTime > allowedIdleTime) {
31 | handleTimeout()
32 | }
33 | }
34 | }
35 | }
36 |
37 | fun resetTimer() {
38 | lastActiveTime = System.currentTimeMillis()
39 | }
40 |
41 | open fun handleMessage(message: IMessage): Boolean {
42 | return false
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/task/RequestTransactionsTask.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.network.peer.task
2 |
3 | import io.horizontalsystems.bitcoincore.models.InventoryItem
4 | import io.horizontalsystems.bitcoincore.network.messages.GetDataMessage
5 | import io.horizontalsystems.bitcoincore.network.messages.IMessage
6 | import io.horizontalsystems.bitcoincore.network.messages.TransactionMessage
7 | import io.horizontalsystems.bitcoincore.storage.FullTransaction
8 |
9 | class RequestTransactionsTask(hashes: List) : PeerTask() {
10 | val hashes = hashes.toMutableList()
11 | var transactions = mutableListOf()
12 |
13 | override val state: String
14 | get() =
15 | "hashesCount: ${hashes.size}; receivedTransactionsCount: ${transactions.size}"
16 |
17 | override fun start() {
18 | val items = hashes.map { hash ->
19 | InventoryItem(InventoryItem.MSG_TX, hash)
20 | }
21 |
22 | requester?.send(GetDataMessage(items))
23 | resetTimer()
24 | }
25 |
26 | override fun handleMessage(message: IMessage): Boolean {
27 | if (message !is TransactionMessage) {
28 | return false
29 | }
30 |
31 | val transaction = message.transaction
32 | val hash = hashes.firstOrNull { it.contentEquals(transaction.header.hash) } ?: return false
33 |
34 | hashes.remove(hash)
35 | transactions.add(transaction)
36 |
37 | if (hashes.isEmpty()) {
38 | listener?.onTaskCompleted(this)
39 | }
40 |
41 | return true
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/rbf/ReplacementTransaction.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.rbf
2 |
3 | import io.horizontalsystems.bitcoincore.models.TransactionInfo
4 | import io.horizontalsystems.bitcoincore.transactions.builder.MutableTransaction
5 |
6 | data class ReplacementTransaction(
7 | internal val mutableTransaction: MutableTransaction,
8 | val info: TransactionInfo,
9 | val replacedTransactionHashes: List
10 | )
11 |
12 | data class ReplacementTransactionInfo(
13 | val replacementTxMinSize: Long,
14 | val feeRange: LongRange
15 | )
16 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/rbf/ReplacementType.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.rbf
2 |
3 | import io.horizontalsystems.bitcoincore.models.Address
4 | import io.horizontalsystems.bitcoincore.models.PublicKey
5 |
6 | sealed class ReplacementType {
7 | object SpeedUp : ReplacementType()
8 | data class Cancel(val address: Address, val publicKey: PublicKey) : ReplacementType()
9 | }
10 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/serializers/BlockHeaderParser.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.serializers
2 |
3 | import io.horizontalsystems.bitcoincore.core.IHasher
4 | import io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable
5 | import io.horizontalsystems.bitcoincore.storage.BlockHeader
6 |
7 | class BlockHeaderParser(private val hasher: IHasher) {
8 |
9 | fun parse(input: BitcoinInputMarkable): BlockHeader {
10 | input.mark()
11 | val payload = input.readBytes(80)
12 | val hash = hasher.hash(payload)
13 | input.reset()
14 |
15 | val version = input.readInt()
16 | val previousBlockHeaderHash = input.readBytes(32)
17 | val merkleRoot = input.readBytes(32)
18 | val timestamp = input.readUnsignedInt()
19 | val bits = input.readUnsignedInt()
20 | val nonce = input.readUnsignedInt()
21 |
22 | return BlockHeader(version, previousBlockHeaderHash, merkleRoot, timestamp, bits, nonce, hash)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/serializers/OutputSerializer.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.serializers
2 |
3 | import io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable
4 | import io.horizontalsystems.bitcoincore.io.BitcoinOutput
5 | import io.horizontalsystems.bitcoincore.models.TransactionOutput
6 |
7 | object OutputSerializer {
8 | fun deserialize(input: BitcoinInputMarkable, vout: Long): TransactionOutput {
9 | val value = input.readLong()
10 | val scriptLength = input.readVarInt() // do not store
11 | val lockingScript = input.readBytes(scriptLength.toInt())
12 | val index = vout.toInt()
13 |
14 | return TransactionOutput(value, index, lockingScript)
15 | }
16 |
17 | fun serialize(output: TransactionOutput): ByteArray {
18 | return BitcoinOutput()
19 | .writeLong(output.value)
20 | .writeVarInt(output.lockingScript.size.toLong())
21 | .write(output.lockingScript)
22 | .toByteArray()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/BlockHashPublicKeyDao.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.storage
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import io.horizontalsystems.bitcoincore.models.BlockHashPublicKey
7 |
8 | @Dao
9 | interface BlockHashPublicKeyDao {
10 |
11 | @Insert(onConflict = OnConflictStrategy.REPLACE)
12 | fun insertAll(users: List)
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/BlockchainStateDao.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.storage
2 |
3 | import androidx.room.*
4 | import io.horizontalsystems.bitcoincore.models.BlockchainState
5 |
6 | @Dao
7 | interface BlockchainStateDao {
8 | @Insert(onConflict = OnConflictStrategy.REPLACE)
9 | fun insert(state: BlockchainState)
10 |
11 | @Query("SELECT * FROM BlockchainState LIMIT 1")
12 | fun getState(): BlockchainState?
13 |
14 | @Delete
15 | fun delete(state: BlockchainState)
16 | }
17 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/DataTypeConverters.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.storage
2 |
3 | import androidx.room.TypeConverter
4 | import io.horizontalsystems.bitcoincore.extensions.hexToByteArray
5 | import io.horizontalsystems.bitcoincore.extensions.toHexString
6 |
7 | class WitnessConverter {
8 |
9 | @TypeConverter
10 | fun fromWitness(list: List): String {
11 | return list.joinToString(", ") {
12 | it.toHexString()
13 | }
14 | }
15 |
16 | @TypeConverter
17 | fun toWitness(data: String): List = when {
18 | data.isEmpty() -> listOf()
19 | else -> data.split(", ").map {
20 | it.hexToByteArray()
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/InvalidTransactionDao.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.storage
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import io.horizontalsystems.bitcoincore.models.InvalidTransaction
8 |
9 | @Dao
10 | interface InvalidTransactionDao {
11 |
12 | @Insert(onConflict = OnConflictStrategy.REPLACE)
13 | fun insert(transaction: InvalidTransaction)
14 |
15 | @Query("DELETE FROM InvalidTransaction")
16 | fun deleteAll()
17 |
18 | @Query("DELETE FROM InvalidTransaction where uid = :uid")
19 | fun delete(uid: String)
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/PeerAddressDao.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.storage
2 |
3 | import androidx.room.*
4 | import io.horizontalsystems.bitcoincore.models.PeerAddress
5 |
6 | @Dao
7 | interface PeerAddressDao {
8 | @Insert(onConflict = OnConflictStrategy.IGNORE)
9 | fun insertAll(peers: List)
10 |
11 | @Query("SELECT * FROM PeerAddress WHERE ip NOT IN(:ips) ORDER BY score ASC, connectionTime ASC LIMIT 1")
12 | fun getLeastScoreFastest(ips: List): PeerAddress?
13 |
14 | @Query("UPDATE PeerAddress SET connectionTime = :time, score = score + 1 WHERE ip = :ip")
15 | fun setSuccessConnectionTime(time: Long, ip: String)
16 |
17 | @Delete
18 | fun delete(peerAddress: PeerAddress)
19 | }
20 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/SentTransactionDao.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.storage
2 |
3 | import androidx.room.*
4 | import io.horizontalsystems.bitcoincore.models.SentTransaction
5 |
6 | @Dao
7 | interface SentTransactionDao {
8 | @Insert(onConflict = OnConflictStrategy.REPLACE)
9 | fun insert(transaction: SentTransaction)
10 |
11 | @Update(onConflict = OnConflictStrategy.REPLACE)
12 | fun update(transaction: SentTransaction)
13 |
14 | @Query("select * from SentTransaction where hash = :hash limit 1")
15 | fun getTransaction(hash: ByteArray): SentTransaction?
16 |
17 | @Delete
18 | fun delete(transaction: SentTransaction)
19 | }
20 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/TransactionMetadataDao.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.storage
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import io.horizontalsystems.bitcoincore.models.TransactionMetadata
8 |
9 | @Dao
10 | interface TransactionMetadataDao {
11 |
12 | @Query("SELECT * FROM `TransactionMetadata` WHERE `transactionHash` IN (:txHashes)")
13 | fun getTransactionMetadata(txHashes: List): List
14 |
15 | @Insert(onConflict = OnConflictStrategy.REPLACE)
16 | fun insert(metadata: TransactionMetadata)
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_10_11.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.storage.migrations
2 |
3 | import androidx.room.migration.Migration
4 | import androidx.sqlite.db.SupportSQLiteDatabase
5 |
6 | object Migration_10_11 : Migration(10, 11) {
7 | override fun migrate(database: SupportSQLiteDatabase) {
8 | database.execSQL("ALTER TABLE `TransactionOutput` ADD COLUMN `failedToSpend` INTEGER DEFAULT 0 NOT NULL")
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_11_12.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.storage.migrations
2 |
3 | import androidx.room.migration.Migration
4 | import androidx.sqlite.db.SupportSQLiteDatabase
5 |
6 | object Migration_11_12 : Migration(11, 12) {
7 | override fun migrate(database: SupportSQLiteDatabase) {
8 | database.execSQL("DELETE FROM `PeerAddress`")
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_13_14.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.storage.migrations
2 |
3 | import androidx.room.migration.Migration
4 | import androidx.sqlite.db.SupportSQLiteDatabase
5 |
6 | object Migration_13_14 : Migration(13, 14) {
7 |
8 | override fun migrate(database: SupportSQLiteDatabase) {
9 | database.execSQL("ALTER TABLE Block ADD COLUMN partial INTEGER DEFAULT 0 NOT NULL")
10 | database.execSQL("UPDATE Block SET partial = 1 WHERE headerHash IN (SELECT headerHash FROM BlockHash)")
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_15_16.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.storage.migrations
2 |
3 | import androidx.room.migration.Migration
4 | import androidx.sqlite.db.SupportSQLiteDatabase
5 |
6 | object Migration_15_16 : Migration(15, 16) {
7 |
8 | override fun migrate(database: SupportSQLiteDatabase) {
9 | database.execSQL("UPDATE `TransactionOutput` SET `lockingScriptPayload` = SUBSTR(`lockingScriptPayload`, 3) WHERE scriptType = 4 and publicKeyPath is not null")
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_16_17.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.storage.migrations
2 |
3 | import androidx.room.migration.Migration
4 | import androidx.sqlite.db.SupportSQLiteDatabase
5 |
6 | object Migration_16_17 : Migration(16, 17) {
7 |
8 | override fun migrate(db: SupportSQLiteDatabase) {
9 | db.execSQL("CREATE TABLE IF NOT EXISTS `BlockHashPublicKey` (`blockHash` BLOB NOT NULL, `publicKeyPath` TEXT NOT NULL, PRIMARY KEY(`blockHash`, `publicKeyPath`), FOREIGN KEY(`blockHash`) REFERENCES `BlockHash`(`headerHash`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`publicKeyPath`) REFERENCES `PublicKey`(`path`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)")
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_17_18.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.storage.migrations
2 |
3 | import androidx.room.migration.Migration
4 | import androidx.sqlite.db.SupportSQLiteDatabase
5 |
6 | object Migration_17_18 : Migration(17, 18) {
7 |
8 | override fun migrate(database: SupportSQLiteDatabase) {
9 | database.execSQL("ALTER TABLE `TransactionInput` RENAME TO `TmpTransactionInput`")
10 |
11 | database.execSQL("CREATE TABLE IF NOT EXISTS `TransactionInput` (`previousOutputTxHash` BLOB NOT NULL, `previousOutputIndex` INTEGER NOT NULL, `sigScript` BLOB NOT NULL, `sequence` INTEGER NOT NULL, `transactionHash` BLOB NOT NULL, `lockingScriptPayload` BLOB, `address` TEXT, `witness` TEXT NOT NULL, PRIMARY KEY(`previousOutputTxHash`, `previousOutputIndex`, `sequence`), FOREIGN KEY(`transactionHash`) REFERENCES `Transaction`(`hash`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)")
12 | database.execSQL("INSERT OR REPLACE INTO `TransactionInput` (`transactionHash`,`lockingScriptPayload`,`address`,`witness`,`previousOutputTxHash`,`previousOutputIndex`,`sigScript`,`sequence`) SELECT `transactionHash`,`lockingScriptPayload`,`address`,`witness`,`previousOutputTxHash`,`previousOutputIndex`,`sigScript`,`sequence` FROM `TmpTransactionInput`")
13 |
14 | database.execSQL("DROP TABLE IF EXISTS `TmpTransactionInput`")
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/SendTransactionsOnPeersSynced.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.transactions
2 |
3 | import io.horizontalsystems.bitcoincore.blocks.IPeerSyncListener
4 |
5 | class SendTransactionsOnPeersSynced(var transactionSender: TransactionSender) : IPeerSyncListener {
6 |
7 | override fun onAllPeersSynced() {
8 | transactionSender.sendPendingTransactions()
9 | }
10 |
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionInvalidator.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.transactions
2 |
3 | import io.horizontalsystems.bitcoincore.blocks.IBlockchainDataListener
4 | import io.horizontalsystems.bitcoincore.core.IStorage
5 | import io.horizontalsystems.bitcoincore.core.ITransactionInfoConverter
6 | import io.horizontalsystems.bitcoincore.models.InvalidTransaction
7 | import io.horizontalsystems.bitcoincore.models.Transaction
8 | import io.horizontalsystems.bitcoincore.storage.FullTransactionInfo
9 |
10 | class TransactionInvalidator(
11 | private val storage: IStorage,
12 | private val transactionInfoConverter: ITransactionInfoConverter,
13 | private val listener: IBlockchainDataListener
14 | ) {
15 |
16 | fun invalidate(transaction: Transaction) {
17 | val invalidTransactionsFullInfo = storage.getDescendantTransactionsFullInfo(transaction.hash)
18 |
19 | if (invalidTransactionsFullInfo.isEmpty()) return
20 |
21 | invalidTransactionsFullInfo.forEach { fullTxInfo ->
22 | fullTxInfo.header.status = Transaction.Status.INVALID
23 | }
24 |
25 | val invalidTransactions = invalidTransactionsFullInfo.map { fullTxInfo ->
26 | val txInfo = transactionInfoConverter.transactionInfo(fullTxInfo)
27 | val serializedTxInfo = txInfo.serialize()
28 | InvalidTransaction(fullTxInfo.header, serializedTxInfo, fullTxInfo.rawTransaction)
29 | }
30 |
31 | storage.moveTransactionToInvalidTransactions(invalidTransactions)
32 | listener.onTransactionsUpdate(listOf(), invalidTransactions, null)
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionSendTimer.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.transactions
2 |
3 | import java.util.concurrent.Executors
4 | import java.util.concurrent.ScheduledFuture
5 | import java.util.concurrent.TimeUnit
6 |
7 | class TransactionSendTimer(private val period: Long) {
8 |
9 | interface Listener {
10 | fun onTimePassed()
11 | }
12 |
13 | var listener: Listener? = null
14 |
15 | private var executor = Executors.newSingleThreadScheduledExecutor()
16 | private var task: ScheduledFuture<*>? = null
17 |
18 | @Synchronized
19 | fun startIfNotRunning() {
20 | if (task == null) {
21 | task = executor.scheduleAtFixedRate({ listener?.onTimePassed() }, period, period, TimeUnit.SECONDS)
22 | }
23 | }
24 |
25 | @Synchronized
26 | fun stop() {
27 | task?.let {
28 | it.cancel(true)
29 | task = null
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionSyncer.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.transactions
2 |
3 | import io.horizontalsystems.bitcoincore.core.IPublicKeyManager
4 | import io.horizontalsystems.bitcoincore.core.IStorage
5 | import io.horizontalsystems.bitcoincore.managers.BloomFilterManager
6 | import io.horizontalsystems.bitcoincore.storage.FullTransaction
7 |
8 | class TransactionSyncer(
9 | private val storage: IStorage,
10 | private val transactionProcessor: PendingTransactionProcessor,
11 | private val invalidator: TransactionInvalidator,
12 | private val publicKeyManager: IPublicKeyManager
13 | ) {
14 |
15 | fun getNewTransactions(): List {
16 | return storage.getNewTransactions()
17 | }
18 |
19 | fun handleRelayed(transactions: List) {
20 | if (transactions.isEmpty()) return
21 |
22 | var needToUpdateBloomFilter = false
23 |
24 | try {
25 | transactionProcessor.processReceived(transactions, false)
26 | } catch (e: BloomFilterManager.BloomFilterExpired) {
27 | needToUpdateBloomFilter = true
28 | }
29 |
30 | if (needToUpdateBloomFilter) {
31 | publicKeyManager.fillGap()
32 | }
33 | }
34 |
35 | fun shouldRequestTransaction(hash: ByteArray): Boolean {
36 | return !storage.isRelayedTransactionExists(hash)
37 | }
38 |
39 | fun handleInvalid(fullTransaction: FullTransaction) {
40 | invalidator.invalidate(fullTransaction.header)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/ECKey.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.transactions.builder
2 |
3 | import io.horizontalsystems.bitcoincore.crypto.schnorr.Schnorr
4 | import io.horizontalsystems.hdwalletkit.ECKey
5 |
6 | fun ECKey.signSchnorr(input: ByteArray, auxRand: ByteArray = ByteArray(32)): ByteArray {
7 | return Schnorr.sign(input, privKeyBytes, auxRand)
8 | }
9 |
10 | fun ECKey.verifySchnorr(input: ByteArray, signature: ByteArray): Boolean {
11 | return Schnorr.verify(input, pubKeyXCoord, signature)
12 | }
13 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/LockTimeSetter.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.transactions.builder
2 |
3 | import io.horizontalsystems.bitcoincore.core.IStorage
4 |
5 | class LockTimeSetter(private val storage: IStorage) {
6 |
7 | fun setLockTime(transaction: MutableTransaction) {
8 | transaction.transaction.lockTime = storage.lastBlock()?.height?.toLong() ?: 0
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/RecipientSetter.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.transactions.builder
2 |
3 | import io.horizontalsystems.bitcoincore.core.IPluginData
4 | import io.horizontalsystems.bitcoincore.core.IRecipientSetter
5 | import io.horizontalsystems.bitcoincore.core.PluginManager
6 | import io.horizontalsystems.bitcoincore.utils.IAddressConverter
7 |
8 | class RecipientSetter(
9 | private val addressConverter: IAddressConverter,
10 | private val pluginManager: PluginManager
11 | ) : IRecipientSetter {
12 |
13 | override fun setRecipient(
14 | mutableTransaction: MutableTransaction,
15 | toAddress: String,
16 | value: Long,
17 | pluginData: Map,
18 | skipChecking: Boolean,
19 | memo: String?
20 | ) {
21 | mutableTransaction.recipientAddress = addressConverter.convert(toAddress)
22 | mutableTransaction.recipientValue = value
23 | mutableTransaction.memo = memo
24 |
25 | pluginManager.processOutputs(mutableTransaction, pluginData, skipChecking)
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/SchnorrInputSigner.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.transactions.builder
2 |
3 | import io.horizontalsystems.bitcoincore.core.IPrivateWallet
4 | import io.horizontalsystems.bitcoincore.models.Transaction
5 | import io.horizontalsystems.bitcoincore.models.TransactionOutput
6 | import io.horizontalsystems.bitcoincore.serializers.TransactionSerializer
7 | import io.horizontalsystems.bitcoincore.storage.InputToSign
8 | import io.horizontalsystems.hdwalletkit.Utils
9 |
10 | class SchnorrInputSigner(
11 | private val hdWallet: IPrivateWallet
12 | ) {
13 | fun sigScriptData(
14 | transaction: Transaction,
15 | inputsToSign: List,
16 | outputs: List,
17 | index: Int
18 | ): List {
19 | val input = inputsToSign[index]
20 | val publicKey = input.previousOutputPublicKey
21 | val tweakedPrivateKey = checkNotNull(hdWallet.privateKey(publicKey.account, publicKey.index, publicKey.external).tweakedOutputKey) {
22 | throw Error.NoPrivateKey()
23 | }
24 | val serializedTransaction = TransactionSerializer.serializeForTaprootSignature(transaction, inputsToSign, outputs, index)
25 |
26 | val signatureHash = Utils.taggedHash("TapSighash", serializedTransaction)
27 | val signature = tweakedPrivateKey.signSchnorr(signatureHash)
28 |
29 | return listOf(signature)
30 | }
31 |
32 | open class Error : Exception() {
33 | class NoPrivateKey : Error()
34 | class NoPreviousOutput : Error()
35 | class NoPreviousOutputAddress : Error()
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/extractors/MyOutputsCache.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.transactions.extractors
2 |
3 | import io.horizontalsystems.bitcoincore.core.HashBytes
4 | import io.horizontalsystems.bitcoincore.core.IStorage
5 | import io.horizontalsystems.bitcoincore.models.TransactionInput
6 | import io.horizontalsystems.bitcoincore.models.TransactionOutput
7 |
8 | class MyOutputsCache {
9 | private val outputsCache = mutableMapOf>()
10 |
11 | fun add(outputs: List) {
12 | for (output in outputs) {
13 | if (output.publicKeyPath != null) {
14 | if (!outputsCache.containsKey(HashBytes(output.transactionHash))) {
15 | outputsCache[HashBytes(output.transactionHash)] = mutableMapOf()
16 | }
17 |
18 | outputsCache[HashBytes(output.transactionHash)]?.set(output.index, output.value)
19 | }
20 | }
21 | }
22 |
23 | fun valueSpentBy(input: TransactionInput): Long? {
24 | return outputsCache[HashBytes(input.previousOutputTxHash)]?.get(input.previousOutputIndex.toInt())
25 | }
26 |
27 | companion object {
28 | fun create(storage: IStorage): MyOutputsCache {
29 | val outputsCache = MyOutputsCache()
30 | val outputs = storage.getMyOutputs()
31 |
32 | outputsCache.add(outputs)
33 |
34 | return outputsCache
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/utils/DirectExecutor.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.utils
2 |
3 | import java.util.concurrent.Executor
4 |
5 | class DirectExecutor : Executor {
6 |
7 | override fun execute(command: Runnable) {
8 | command.run()
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/utils/MainThreadExecutor.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.utils
2 |
3 | import android.os.Handler
4 | import android.os.Looper
5 | import java.util.concurrent.Executor
6 |
7 | class MainThreadExecutor : Executor {
8 |
9 | private val uiHandler = Handler(Looper.getMainLooper())
10 |
11 | override fun execute(command: Runnable) {
12 | uiHandler.post(command)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/utils/NetworkUtils.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.utils
2 |
3 | import io.horizontalsystems.bitcoincore.extensions.toHexString
4 | import java.net.*
5 |
6 | object NetworkUtils {
7 |
8 | fun getLocalInetAddress(): InetAddress {
9 | try {
10 | return InetAddress.getLocalHost()
11 | } catch (e: UnknownHostException) {
12 | throw RuntimeException(e)
13 | }
14 | }
15 |
16 | fun getIPv6(inetAddr: InetAddress): ByteArray {
17 | val ip = inetAddr.address
18 | if (ip.size == 16) {
19 | return ip
20 | }
21 |
22 | if (ip.size == 4) {
23 | val ipv6 = ByteArray(16)
24 | ipv6[10] = -1
25 | ipv6[11] = -1
26 | System.arraycopy(ip, 0, ipv6, 12, 4)
27 | return ipv6
28 | }
29 |
30 | throw RuntimeException("Bad IP: " + ip.toHexString())
31 | }
32 |
33 | fun createSocket(): Socket {
34 |
35 | val socksProxyHost = System.getProperty("socksProxyHost")
36 | val socksProxyPort = System.getProperty("socksProxyPort")?.toIntOrNull()
37 |
38 | return if (socksProxyHost != null && socksProxyPort != null) {
39 | val socketAddress = InetSocketAddress.createUnresolved(socksProxyHost, socksProxyPort)
40 | val proxy = Proxy(Proxy.Type.SOCKS, socketAddress)
41 | Socket(proxy)
42 | } else {
43 | Socket()
44 | }
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/utils/TransactionDataSorters.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.utils
2 |
3 | import io.horizontalsystems.bitcoincore.core.ITransactionDataSorter
4 | import io.horizontalsystems.bitcoincore.models.TransactionOutput
5 | import io.horizontalsystems.bitcoincore.storage.UnspentOutput
6 | import java.util.*
7 |
8 | class Bip69Sorter : ITransactionDataSorter {
9 | override fun sortOutputs(outputs: List): List {
10 | Collections.sort(outputs, Bip69.outputComparator)
11 | return outputs
12 | }
13 |
14 | override fun sortUnspents(unspents: List): List {
15 | Collections.sort(unspents, Bip69.inputComparator)
16 | return unspents
17 | }
18 | }
19 |
20 | class ShuffleSorter : ITransactionDataSorter {
21 | override fun sortOutputs(outputs: List): List {
22 | return outputs.shuffled()
23 | }
24 |
25 | override fun sortUnspents(unspents: List): List {
26 | return unspents.shuffled()
27 | }
28 | }
29 |
30 | class StraightSorter : ITransactionDataSorter {
31 | override fun sortOutputs(outputs: List): List {
32 | return outputs
33 | }
34 |
35 | override fun sortUnspents(unspents: List): List {
36 | return unspents
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/bitcoincore/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Bitcoin Core
3 |
4 |
--------------------------------------------------------------------------------
/bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/RxTestRule.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore
2 |
3 | import io.reactivex.plugins.RxJavaPlugins
4 | import io.reactivex.schedulers.Schedulers
5 |
6 | object RxTestRule {
7 |
8 | fun setup() {
9 | //https://medium.com/@fabioCollini/testing-asynchronous-rxjava-code-using-mockito-8ad831a16877
10 | RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
11 | RxJavaPlugins.setComputationSchedulerHandler { Schedulers.trampoline() }
12 | RxJavaPlugins.setNewThreadSchedulerHandler { Schedulers.trampoline() }
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/BitsValidatorTest.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.blocks.validators
2 |
3 | import com.nhaarman.mockitokotlin2.mock
4 | import com.nhaarman.mockitokotlin2.whenever
5 | import io.horizontalsystems.bitcoincore.models.Block
6 | import org.junit.jupiter.api.Assertions.assertDoesNotThrow
7 | import org.junit.jupiter.api.assertThrows
8 | import org.spekframework.spek2.Spek
9 | import org.spekframework.spek2.style.specification.describe
10 |
11 | object BitsValidatorTest : Spek({
12 | lateinit var validator: BitsValidator
13 | val block = mock()
14 | val previousBlock = mock()
15 |
16 | beforeEachTest {
17 | validator = BitsValidator()
18 | }
19 |
20 | describe("#validate") {
21 |
22 | it("passes when block and prev block bits are equal") {
23 | whenever(block.bits).thenReturn(1)
24 | whenever(previousBlock.bits).thenReturn(1)
25 |
26 | assertDoesNotThrow {
27 | validator.validate(block, previousBlock)
28 | }
29 | }
30 |
31 | it("fails when block and prev block bits are not equal") {
32 | whenever(block.bits).thenReturn(1)
33 | whenever(previousBlock.bits).thenReturn(2)
34 |
35 | assertThrows {
36 | validator.validate(block, previousBlock)
37 | }
38 | }
39 |
40 | }
41 | })
42 |
--------------------------------------------------------------------------------
/bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/ProofOfWorkValidatorTest.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.blocks.validators
2 |
3 | import io.horizontalsystems.bitcoincore.Fixtures
4 | import io.horizontalsystems.bitcoincore.crypto.CompactBits
5 | import org.junit.jupiter.api.Assertions.assertDoesNotThrow
6 | import org.junit.jupiter.api.assertThrows
7 | import org.spekframework.spek2.Spek
8 | import org.spekframework.spek2.style.specification.describe
9 | import java.math.BigInteger
10 |
11 | object ProofOfWorkValidatorTest : Spek({
12 | lateinit var validator: ProofOfWorkValidator
13 |
14 | beforeEachTest {
15 | validator = ProofOfWorkValidator()
16 | }
17 |
18 | describe("#validate") {
19 | it("passes when proof of work is valid") {
20 | val block = Fixtures.block2
21 | val previousBlock = Fixtures.block1
22 | assertDoesNotThrow {
23 | validator.validate(block, previousBlock)
24 | }
25 | }
26 |
27 | it("fails when proof of work is not valid") {
28 | val block = Fixtures.block2
29 | val previousBlock = Fixtures.block1
30 | block.bits = CompactBits.encode(BigInteger(block.headerHash).minus(BigInteger.valueOf(1L)))
31 |
32 | assertThrows {
33 | validator.validate(block, previousBlock)
34 | }
35 | }
36 | }
37 |
38 | })
39 |
--------------------------------------------------------------------------------
/bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/managers/SyncManagerTest.kt:
--------------------------------------------------------------------------------
1 | //package io.horizontalsystems.bitcoincore.managers
2 | //
3 | //import com.nhaarman.mockitokotlin2.mock
4 | //import com.nhaarman.mockitokotlin2.verify
5 | //import io.horizontalsystems.bitcoincore.network.peer.PeerGroup
6 | //import org.mockito.Mockito.reset
7 | //import org.spekframework.spek2.Spek
8 | //import org.spekframework.spek2.style.specification.describe
9 | //
10 | //object SyncManagerTest : Spek({
11 | //
12 | // val peerGroup = mock()
13 | // val initialSyncer = mock()
14 | //
15 | // val syncManager by memoized {
16 | // SyncManager(peerGroup, initialSyncer)
17 | // }
18 | //
19 | // afterEachTest {
20 | // reset(peerGroup, initialSyncer)
21 | // }
22 | //
23 | // describe("#start") {
24 | // it("starts :initialSyncer") {
25 | // syncManager.start()
26 | //
27 | // verify(initialSyncer).sync()
28 | // }
29 | // }
30 | //
31 | // describe("#stop") {
32 | // it("stops :peerGroup") {
33 | // syncManager.stop()
34 | //
35 | // verify(peerGroup).stop()
36 | // }
37 | //
38 | // it("stops :initialSyncer") {
39 | // syncManager.stop()
40 | //
41 | // verify(peerGroup).stop()
42 | // }
43 | // }
44 | //
45 | // describe("#onSyncingFinished") {
46 | // it("starts peer group") {
47 | // syncManager.onSyncingFinished()
48 | //
49 | // verify(peerGroup).start()
50 | // }
51 | // }
52 | //
53 | //})
54 |
--------------------------------------------------------------------------------
/bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/utils/NetworkUtilsTest.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoincore.utils
2 |
3 | import okhttp3.OkHttpClient
4 | import okhttp3.Request
5 | import org.junit.jupiter.api.Test
6 |
7 | import org.junit.jupiter.api.Assertions.*
8 | import java.net.URL
9 | import javax.net.ssl.SSLHandshakeException
10 |
11 | internal class NetworkUtilsTest {
12 |
13 | // System is Unable to find valid certification path to below URL
14 | private val CERT_ERROR_TEST_URL = "https://cashexplorer.bitcoin.com/api/sync/"
15 |
16 | @Test
17 | fun testUnsafeHttpRequest() {
18 |
19 | assertThrows(SSLHandshakeException::class.java) { doSafeHttpRequest() }
20 | assertThrows(SSLHandshakeException::class.java) { doUrlConnectionRequest() }
21 | }
22 |
23 | private fun doSafeHttpRequest() {
24 | doOkHttpRequest(OkHttpClient.Builder().build())
25 | }
26 |
27 | private fun doUrlConnectionRequest() {
28 | URL(CERT_ERROR_TEST_URL)
29 | .openConnection()
30 | .apply {
31 | connectTimeout = 5000
32 | readTimeout = 60000
33 | setRequestProperty("Accept", "application/json")
34 | }.getInputStream()
35 | .use {
36 | //Success
37 | }
38 | }
39 |
40 | private fun doOkHttpRequest(httpClient: OkHttpClient) {
41 | val request = Request.Builder().url(CERT_ERROR_TEST_URL).build()
42 |
43 | return httpClient.newCall(request)
44 | .execute()
45 | .use {
46 | //success
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/bitcoincore/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker:
--------------------------------------------------------------------------------
1 | mock-maker-inline
--------------------------------------------------------------------------------
/bitcoinkit/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/bitcoinkit/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/bitcoinkit/src/main/kotlin/io/horizontalsystems/bitcoinkit/MainNet.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoinkit
2 |
3 | import io.horizontalsystems.bitcoincore.network.Network
4 |
5 | /**
6 | * Extends from the abstract Network class and overrides all variables. Configures connection to the MainNet.
7 | */
8 | class MainNet : Network() {
9 |
10 | override var port: Int = 8333
11 |
12 | override var magic: Long = 0xd9b4bef9L
13 | override var bip32HeaderPub: Int = 0x0488B21E // The 4 byte header that serializes in base58 to "xpub".
14 | override var bip32HeaderPriv: Int = 0x0488ADE4 // The 4 byte header that serializes in base58 to "xprv"
15 | override var addressVersion: Int = 0
16 | override var addressSegwitHrp: String = "bc"
17 | override var addressScriptVersion: Int = 5
18 | override var coinType: Int = 0
19 | override val blockchairChainId: String = "bitcoin"
20 |
21 | override val maxBlockSize = 1_000_000
22 | override val dustRelayTxFee = 3000 // https://github.com/bitcoin/bitcoin/blob/c536dfbcb00fb15963bf5d507b7017c241718bf6/src/policy/policy.h#L50
23 |
24 | override var dnsSeeds = listOf(
25 | "x5.seed.bitcoin.sipa.be", // Pieter Wuille
26 | "x5.dnsseed.bluematt.me", // Matt Corallo
27 | "x5.seed.bitcoinstats.com", // Chris Decker
28 | "x5.seed.btc.petertodd.org", // Peter Todd
29 | "x5.seed.bitcoin.sprovoost.nl", // Sjors Provoost
30 | "x5.seed.bitnodes.io", // Addy Yeow
31 | "x5.dnsseed.emzy.de", // Stephan Oeste
32 | "x5.seed.bitcoin.wiz.biz" // Jason Maurice
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/bitcoinkit/src/main/kotlin/io/horizontalsystems/bitcoinkit/RegTest.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoinkit
2 |
3 | import io.horizontalsystems.bitcoincore.network.Network
4 | /**
5 | *
6 | *
7 | * Extends from the abstract Network class and overrides all variables. Configures connection to the RegTest.
8 | */
9 | class RegTest : Network() {
10 | override var port: Int = 18444
11 |
12 | override var magic: Long = 0xdab5bffa
13 | override var bip32HeaderPub: Int = 0x043587CF
14 | override var bip32HeaderPriv: Int = 0x04358394
15 | override var addressVersion: Int = 111
16 | override var addressSegwitHrp: String = "tb"
17 | override var addressScriptVersion: Int = 196
18 | override var coinType: Int = 1
19 | override val blockchairChainId: String = ""
20 |
21 | override val maxBlockSize = 1_000_000
22 | override val dustRelayTxFee = 3000 // https://github.com/bitcoin/bitcoin/blob/c536dfbcb00fb15963bf5d507b7017c241718bf6/src/policy/policy.h#L50
23 | override val syncableFromApi = false
24 |
25 | override var dnsSeeds = listOf(
26 | "btc-regtest.blocksdecoded.com",
27 | "btc01-regtest.blocksdecoded.com",
28 | "btc02-regtest.blocksdecoded.com",
29 | "btc03-regtest.blocksdecoded.com"
30 | )
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/bitcoinkit/src/main/kotlin/io/horizontalsystems/bitcoinkit/TestNet.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoinkit
2 |
3 | import io.horizontalsystems.bitcoincore.network.Network
4 | /**
5 | * Extends from the abstract Network class and overrides all variables. Configures connection to the TestNet.
6 | */
7 | class TestNet : Network() {
8 |
9 | override var port: Int = 18333
10 |
11 | override var magic: Long = 0x0709110B
12 | override var bip32HeaderPub: Int = 0x043587CF
13 | override var bip32HeaderPriv: Int = 0x04358394
14 | override var addressVersion: Int = 111
15 | override var addressSegwitHrp: String = "tb"
16 | override var addressScriptVersion: Int = 196
17 | override var coinType: Int = 1
18 | override val blockchairChainId: String = "bitcoin/testnet"
19 |
20 | override val maxBlockSize = 1_000_000
21 | override val dustRelayTxFee = 3000 // https://github.com/bitcoin/bitcoin/blob/c536dfbcb00fb15963bf5d507b7017c241718bf6/src/policy/policy.h#L50
22 |
23 | override var dnsSeeds = listOf(
24 | "x5.testnet-seed.bitcoin.jonasschnelli.ch",
25 | "x5.seed.tbtc.petertodd.org",
26 | "x5.seed.testnet.bitcoin.sprovoost.nl",
27 | "testnet-seed.bluematt.me"
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/bitcoinkit/src/main/kotlin/io/horizontalsystems/bitcoinkit/core/DataProvider.kt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/horizontalsystems/bitcoin-kit-android/230e3625b6914e1ea6cd3801b77d6bc03cf1df92/bitcoinkit/src/main/kotlin/io/horizontalsystems/bitcoinkit/core/DataProvider.kt
--------------------------------------------------------------------------------
/bitcoinkit/src/main/kotlin/io/horizontalsystems/bitcoinkit/utils/AddressConverter.kt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/horizontalsystems/bitcoin-kit-android/230e3625b6914e1ea6cd3801b77d6bc03cf1df92/bitcoinkit/src/main/kotlin/io/horizontalsystems/bitcoinkit/utils/AddressConverter.kt
--------------------------------------------------------------------------------
/bitcoinkit/src/main/resources/MainNet-bip44.checkpoint:
--------------------------------------------------------------------------------
1 | 02000000ba3f2b4208ec0495b2e3743465cae2b44d8f1c778b44cf6b0000000000000000d287e52e8045c060c1cee47d1cc7559c7b8ab8db580539fb55fc579a998ea14efe0e50538c9d001926c0c180a08504003f72e59e0db5b38e5210369dc2fb4831ab1e81f3b5dbec3d0000000000000000
2 |
--------------------------------------------------------------------------------
/bitcoinkit/src/main/resources/MainNet.checkpoint:
--------------------------------------------------------------------------------
1 | 00403c2bb73b81f782415b55cde8f8080962714e26ce401e36a301000000000000000000fe9a0b00705125277309836d8a6e45d0c13925128f04b0faaafb7654ddacf18d3285bb67b18b0217994d48f920810d0041c13b6b7686350ffe3852fa1e5766b0fce41ee1918801000000000000000000
2 |
--------------------------------------------------------------------------------
/bitcoinkit/src/main/resources/TestNet-bip44.checkpoint:
--------------------------------------------------------------------------------
1 | 0200000097f2b61897ba2bed756cca30058bcc1c2dfbb4ed0e962f47f749dc03000000006b80079a1eda8071424e294fa56849370e331c8ff7e95034576c9789c8db0fa6da551153ab80011c9bdaca25a00b03009a259d2c34148908a4e71853349f033fd7ac5cbc29bd833adebb000000000000
2 |
--------------------------------------------------------------------------------
/bitcoinkit/src/main/resources/TestNet.checkpoint:
--------------------------------------------------------------------------------
1 | 00607e2a08eab9b236d7b564a0ca280f0bf34c4ee75aae2a7967d0c5b100000000000000ba65fa9ea31062b73d85e6b7aa743e81329e0bb48b7480acd79367e1b3b9d4664c1d1067c0ff3f1917f4a95620b23000160fd5a8c341763895790d4f270a9fa145d3708c8c74e0823900000000000000
2 |
--------------------------------------------------------------------------------
/bitcoinkit/src/test/kotlin/io/horizontalsystems/bitcoinkit/MainNetTest.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.bitcoinkit
2 |
3 | import io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable
4 | import org.junit.Assert.assertEquals
5 | import org.junit.Before
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 | import org.powermock.core.classloader.annotations.PrepareForTest
9 | import org.powermock.modules.junit4.PowerMockRunner
10 |
11 | @RunWith(PowerMockRunner::class)
12 | @PrepareForTest(MainNet::class)
13 |
14 | class MainNetTest {
15 |
16 | private lateinit var network: MainNet
17 |
18 | @Before
19 | fun setup() {
20 | network = MainNet()
21 | }
22 |
23 | @Test
24 | fun packetMagic() {
25 | val stream = BitcoinInputMarkable(byteArrayOf(
26 | 0xf9.toByte(),
27 | 0xbe.toByte(),
28 | 0xb4.toByte(),
29 | 0xd9.toByte()
30 | ))
31 |
32 | val magic = stream.readUnsignedInt()
33 | assertEquals(magic, network.magic)
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/bitcoinkit/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker:
--------------------------------------------------------------------------------
1 | mock-maker-inline
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.8.0'
5 | repositories {
6 | google()
7 | mavenCentral()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:8.3.1'
11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | mavenCentral()
22 | maven { url "https://jitpack.io" }
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/dashkit/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /.cxx
--------------------------------------------------------------------------------
/dashkit/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.6.0)
2 |
3 | add_subdirectory(dashj-bls)
4 |
--------------------------------------------------------------------------------
/dashkit/cpp/config.h:
--------------------------------------------------------------------------------
1 | #define HAVE_DECL_BE64ENC 0
2 | #define HAVE_MMAP 1
3 |
4 | #ifndef __ANDROID__
5 | #define HAVE_POSIX_MEMALIGN 1
6 | #endif
7 |
8 | #ifdef __ANDROID__
9 | #include
10 | #endif
11 |
--------------------------------------------------------------------------------
/dashkit/cpp/dashj-bls/pthread.c:
--------------------------------------------------------------------------------
1 | //
2 | // Created by hashengineering on 12/1/18.
3 | //
4 | int dummy = 0;
5 |
--------------------------------------------------------------------------------
/dashkit/cpp/dashj-bls/stdio.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by hashengineering on 12/1/18.
3 | //
4 |
5 | #include
6 |
7 | //
8 | // This file adds stdin, stdout and stderr for API < 23
9 | //
10 |
11 | #if __ANDROID_API__ < __ANDROID_API_M__
12 | #undef stdin
13 | #undef stdout
14 | #undef stderr
15 | extern "C" {
16 |
17 | FILE * stdin (&__sF[0]);
18 | FILE * stdout (&__sF[1]);
19 | FILE * stderr (&__sF[2]);
20 |
21 | }
22 | #endif
--------------------------------------------------------------------------------
/dashkit/libs/dashj-bls-0.15.3.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/horizontalsystems/bitcoin-kit-android/230e3625b6914e1ea6cd3801b77d6bc03cf1df92/dashkit/libs/dashj-bls-0.15.3.jar
--------------------------------------------------------------------------------
/dashkit/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/dashkit/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/DashKitErrors.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit
2 |
3 | object DashKitErrors {
4 | sealed class LockVoteValidation : Exception() {
5 | class MasternodeNotFound : LockVoteValidation()
6 | class MasternodeNotInTop : LockVoteValidation()
7 | class TxInputNotFound : LockVoteValidation()
8 | class SignatureNotValid : LockVoteValidation()
9 | }
10 |
11 | sealed class ISLockValidation : Exception() {
12 | class SignatureNotValid : ISLockValidation()
13 | class QuorumNotFound : ISLockValidation()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/IDashStorage.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit
2 |
3 | import io.horizontalsystems.bitcoincore.models.Block
4 | import io.horizontalsystems.bitcoincore.models.TransactionInput
5 | import io.horizontalsystems.dashkit.models.*
6 |
7 | interface IDashStorage {
8 | fun getBlock(blockHash: ByteArray): Block?
9 | fun instantTransactionHashes(): List
10 | fun instantTransactionInputs(txHash: ByteArray): List
11 | fun getTransactionInputs(txHash: ByteArray): List
12 | fun addInstantTransactionInput(instantTransactionInput: InstantTransactionInput)
13 | fun addInstantTransactionHash(txHash: ByteArray)
14 | fun removeInstantTransactionInputs(txHash: ByteArray)
15 | fun isTransactionExists(txHash: ByteArray): Boolean
16 | fun getQuorumsByType(quorumType: QuorumType): List
17 |
18 | var masternodes: List
19 | var masternodeListState: MasternodeListState?
20 | var quorums: List
21 | }
22 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/IInstantTransactionDelegate.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit
2 |
3 | // TODO Rename to listener
4 | interface IInstantTransactionDelegate {
5 | fun onUpdateInstant(transactionHash: ByteArray)
6 | }
7 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/IMerkleHasher.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit
2 |
3 | interface IMerkleHasher {
4 | fun hash(first: ByteArray, second: ByteArray) : ByteArray
5 | }
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/InventoryType.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit
2 |
3 | object InventoryType {
4 | const val MSG_TXLOCK_REQUEST = 4
5 | const val MSG_TXLOCK_VOTE = 5
6 | const val MSG_ISLOCK = 30
7 | }
8 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/MainNetDash.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit
2 |
3 | import io.horizontalsystems.bitcoincore.network.Network
4 |
5 | class MainNetDash : Network() {
6 |
7 | override val protocolVersion = 70228
8 | override val noBloomVersion = 70201
9 |
10 | override var port: Int = 9999
11 |
12 | override var magic: Long = 0xbd6b0cbf
13 | override var bip32HeaderPub: Int = 0x0488B21E // The 4 byte header that serializes in base58 to "xpub".
14 | override var bip32HeaderPriv: Int = 0x0488ADE4 // The 4 byte header that serializes in base58 to "xprv"
15 | override var addressVersion: Int = 76
16 | override var addressSegwitHrp: String = "bc"
17 | override var addressScriptVersion: Int = 16
18 | override var coinType: Int = 5
19 | override val blockchairChainId: String = "dash"
20 |
21 | override val maxBlockSize = 1_000_000
22 | override val dustRelayTxFee = 1000 // https://github.com/dashpay/dash/blob/master/src/policy/policy.h#L36
23 |
24 | override var dnsSeeds = listOf(
25 | "dnsseed.dash.org",
26 | "dnsseed.dashdot.io",
27 | "dnsseed.masternode.io"
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/TestNetDash.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit
2 |
3 | import io.horizontalsystems.bitcoincore.network.Network
4 |
5 | class TestNetDash : Network() {
6 |
7 | override val protocolVersion = 70214
8 | override val noBloomVersion = 70201
9 |
10 | override var port: Int = 19999
11 |
12 | override var magic: Long = 0xffcae2ce
13 | override var bip32HeaderPub: Int = 0x0488B21E // The 4 byte header that serializes in base58 to "xpub".
14 | override var bip32HeaderPriv: Int = 0x0488ADE4 // The 4 byte header that serializes in base58 to "xprv"
15 | override var addressVersion: Int = 140
16 | override var addressSegwitHrp: String = "bc"
17 | override var addressScriptVersion: Int = 19
18 | override var coinType: Int = 1
19 | override val blockchairChainId: String = ""
20 |
21 | override val maxBlockSize = 1_000_000
22 | override val dustRelayTxFee = 1000 // https://github.com/dashpay/dash/blob/master/src/policy/policy.h#L36
23 |
24 | override var dnsSeeds = listOf(
25 | "testnet-seed.dashdot.io",
26 | "test.dnsseed.masternode.io"
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/X11Hasher.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit
2 |
3 | import fr.cryptohash.*
4 | import io.horizontalsystems.bitcoincore.core.IHasher
5 | import java.util.*
6 |
7 | class X11Hasher : IHasher {
8 | private val algorithms = listOf(
9 | BLAKE512(),
10 | BMW512(),
11 | Groestl512(),
12 | Skein512(),
13 | JH512(),
14 | Keccak512(),
15 | Luffa512(),
16 | CubeHash512(),
17 | SHAvite512(),
18 | SIMD512(),
19 | ECHO512()
20 | )
21 |
22 | override fun hash(data: ByteArray): ByteArray {
23 | var hash = data
24 |
25 | algorithms.forEach {
26 | hash = it.digest(hash)
27 | }
28 |
29 | return hash.copyOfRange(0, 32)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/core/DoubleSha256Hasher.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.core
2 |
3 | import io.horizontalsystems.bitcoincore.core.IHasher
4 | import io.horizontalsystems.bitcoincore.utils.HashUtils
5 |
6 | class SingleSha256Hasher : IHasher {
7 | override fun hash(data: ByteArray): ByteArray {
8 | return HashUtils.sha256(data)
9 | }
10 | }
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/BLS.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.instantsend
2 |
3 | import org.dashj.bls.InsecureSignature
4 | import org.dashj.bls.JNI
5 | import org.dashj.bls.PublicKey
6 | import java.util.logging.Logger
7 |
8 | class BLS {
9 | private val logger = Logger.getLogger("BLS")
10 |
11 | init {
12 | System.loadLibrary(JNI.LIBRARY_NAME)
13 | }
14 |
15 | fun verifySignature(pubKeyOperator: ByteArray, vchMasternodeSignature: ByteArray, hash: ByteArray): Boolean {
16 | return try {
17 | val pk = PublicKey.FromBytes(pubKeyOperator)
18 | val insecureSignature = InsecureSignature.FromBytes(vchMasternodeSignature)
19 |
20 | insecureSignature.Verify(hash, pk)
21 | } catch (e: Exception) {
22 | logger.severe("Verifying BLS signature failed with exception: $e")
23 |
24 | false
25 | }
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/InstantSendFactory.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.instantsend
2 |
3 | import io.horizontalsystems.dashkit.models.InstantTransactionInput
4 | import java.util.*
5 |
6 | class InstantSendFactory {
7 |
8 | fun instantTransactionInput(txHash: ByteArray, inputTxHash: ByteArray, voteCount: Int, blockHeight: Int?) : InstantTransactionInput {
9 | val timeCreated = Date().time / 1000
10 |
11 | return InstantTransactionInput(txHash, inputTxHash, timeCreated, voteCount, blockHeight)
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/InstantSendLockValidator.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.instantsend
2 |
3 | import io.horizontalsystems.bitcoincore.io.BitcoinOutput
4 | import io.horizontalsystems.bitcoincore.utils.HashUtils
5 | import io.horizontalsystems.dashkit.DashKitErrors
6 | import io.horizontalsystems.dashkit.managers.QuorumListManager
7 | import io.horizontalsystems.dashkit.messages.ISLockMessage
8 | import io.horizontalsystems.dashkit.models.QuorumType
9 |
10 | class InstantSendLockValidator(
11 | private val quorumListManager: QuorumListManager,
12 | private val bls: BLS
13 | ) {
14 |
15 | @Throws
16 | fun validate(islock: ISLockMessage) {
17 | // 01. Select quorum
18 | val quorum = quorumListManager.getQuorum(QuorumType.LLMQ_50_60, islock.requestId)
19 |
20 | // 02. Make signId data to verify signature
21 | val signIdPayload = BitcoinOutput()
22 | .writeByte(quorum.type)
23 | .write(quorum.quorumHash)
24 | .write(islock.requestId)
25 | .write(islock.txHash)
26 | .toByteArray()
27 |
28 | val signId = HashUtils.doubleSha256(signIdPayload)
29 |
30 | // 03. Verify signature by BLS
31 | if (!bls.verifySignature(quorum.quorumPublicKey, islock.sign, signId)) {
32 | throw DashKitErrors.ISLockValidation.SignatureNotValid()
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/QuorumMasternode.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.instantsend
2 |
3 | import io.horizontalsystems.dashkit.models.Masternode
4 |
5 | class QuorumMasternode(val quorumHash: ByteArray, val masternode: Masternode) : Comparable {
6 |
7 | override fun compareTo(other: QuorumMasternode): Int {
8 | for (i in 0 until quorumHash.size) {
9 | val b1: Int = quorumHash[i].toInt() and 0xff
10 | val b2: Int = other.quorumHash[i].toInt() and 0xff
11 |
12 | val res = b1.compareTo(b2)
13 | if (res != 0) return res
14 | }
15 |
16 | return 0
17 | }
18 | }
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/instantsendlock/InstantSendLockManager.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.instantsend.instantsendlock
2 |
3 | import io.horizontalsystems.bitcoincore.core.HashBytes
4 | import io.horizontalsystems.dashkit.instantsend.InstantSendLockValidator
5 | import io.horizontalsystems.dashkit.messages.ISLockMessage
6 |
7 | class InstantSendLockManager(private val instantSendLockValidator: InstantSendLockValidator) {
8 | private val relayedLocks = mutableMapOf()
9 |
10 | fun add(relayed: ISLockMessage) {
11 | relayedLocks[HashBytes(relayed.txHash)] = relayed
12 | }
13 |
14 | fun takeRelayedLock(txHash: ByteArray): ISLockMessage? {
15 | relayedLocks[HashBytes(txHash)]?.let {
16 | relayedLocks.remove(HashBytes(txHash))
17 | return it
18 | }
19 | return null
20 | }
21 |
22 | @Throws
23 | fun validate(isLock: ISLockMessage) {
24 | instantSendLockValidator.validate(isLock)
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/transactionlockvote/TransactionLockVoteManager.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.instantsend.transactionlockvote
2 |
3 | import io.horizontalsystems.dashkit.instantsend.TransactionLockVoteValidator
4 | import io.horizontalsystems.dashkit.messages.TransactionLockVoteMessage
5 |
6 | class TransactionLockVoteManager(private val transactionLockVoteValidator: TransactionLockVoteValidator) {
7 | val relayedLockVotes = mutableListOf()
8 | val checkedLockVotes = mutableListOf()
9 |
10 | fun takeRelayedLockVotes(txHash: ByteArray): List {
11 | val votes = relayedLockVotes.filter {
12 | it.txHash.contentEquals(txHash)
13 | }
14 | relayedLockVotes.removeAll(votes)
15 | return votes
16 | }
17 |
18 | fun addRelayed(vote: TransactionLockVoteMessage) {
19 | relayedLockVotes.add(vote)
20 | }
21 |
22 | fun addChecked(vote: TransactionLockVoteMessage) {
23 | checkedLockVotes.add(vote)
24 | }
25 |
26 | fun processed(lvHash: ByteArray): Boolean {
27 | return relayedLockVotes.any { it.hash.contentEquals(lvHash) } || checkedLockVotes.any { it.hash.contentEquals(lvHash) }
28 | }
29 |
30 | @Throws
31 | fun validate(lockVote: TransactionLockVoteMessage) {
32 | // validate masternode in top 10 masternodes for quorumModifier
33 | transactionLockVoteValidator.validate(lockVote.quorumModifierHash, lockVote.masternodeProTxHash, lockVote.vchMasternodeSignature, lockVote.hash)
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/managers/ConfirmedUnspentOutputProvider.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.managers
2 |
3 | import io.horizontalsystems.bitcoincore.core.IStorage
4 | import io.horizontalsystems.bitcoincore.managers.IUnspentOutputProvider
5 | import io.horizontalsystems.bitcoincore.storage.UnspentOutput
6 | import io.horizontalsystems.bitcoincore.storage.UtxoFilters
7 |
8 | class ConfirmedUnspentOutputProvider(private val storage: IStorage, private val confirmationsThreshold: Int) : IUnspentOutputProvider {
9 | override fun getSpendableUtxo(filters: UtxoFilters): List {
10 | val lastBlockHeight = storage.lastBlock()?.height ?: 0
11 |
12 | return storage.getUnspentOutputs().filter {
13 | isOutputConfirmed(it, lastBlockHeight) && filters.filterUtxo(it, storage)
14 | }
15 | }
16 |
17 | private fun isOutputConfirmed(unspentOutput: UnspentOutput, lastBlockHeight: Int): Boolean {
18 | val block = unspentOutput.block ?: return false
19 |
20 | return block.height <= lastBlockHeight - confirmationsThreshold + 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/managers/MasternodeSortedList.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.managers
2 |
3 | import io.horizontalsystems.dashkit.models.Masternode
4 |
5 | class MasternodeSortedList {
6 | private val masternodeList = mutableListOf()
7 |
8 | val masternodes: List
9 | get() = masternodeList.sorted()
10 |
11 | fun add(masternodes: List) {
12 | masternodeList.removeAll(masternodes)
13 | masternodeList.addAll(masternodes)
14 | }
15 |
16 | fun remove(proRegTxHashes: List) {
17 | proRegTxHashes.forEach { hash ->
18 | val index = masternodeList.indexOfFirst { masternode ->
19 | masternode.proRegTxHash.contentEquals(hash)
20 | }
21 |
22 | if (index != -1) {
23 | masternodeList.removeAt(index)
24 | }
25 | }
26 | }
27 |
28 | fun removeAll() {
29 | masternodeList.clear()
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/managers/QuorumSortedList.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.managers
2 |
3 | import io.horizontalsystems.dashkit.models.Quorum
4 |
5 | class QuorumSortedList {
6 | private val quorumList = mutableListOf()
7 |
8 | val quorums: List
9 | get() = quorumList.sorted()
10 |
11 |
12 | fun add(quorums: List) {
13 | quorumList.removeAll(quorums)
14 | quorumList.addAll(quorums)
15 | }
16 |
17 | fun remove(quorums: List>) {
18 | quorums.forEach { (type, quorumHash) ->
19 | val index = quorumList.indexOfFirst { quorum ->
20 | quorum.type == type && quorum.quorumHash.contentEquals(quorumHash)
21 | }
22 |
23 | if (index != -1) {
24 | quorumList.removeAt(index)
25 | }
26 | }
27 | }
28 |
29 | fun removeAll() {
30 | quorumList.clear()
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/masternodelist/MasternodeCbTxHasher.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.masternodelist
2 |
3 | import io.horizontalsystems.bitcoincore.core.HashBytes
4 | import io.horizontalsystems.bitcoincore.core.IHasher
5 | import io.horizontalsystems.dashkit.models.CoinbaseTransaction
6 | import io.horizontalsystems.dashkit.models.CoinbaseTransactionSerializer
7 |
8 | class MasternodeCbTxHasher(private val coinbaseTransactionSerializer: CoinbaseTransactionSerializer, private val hasher: IHasher) {
9 |
10 | fun hash(coinbaseTransaction: CoinbaseTransaction): HashBytes {
11 | val serialized = coinbaseTransactionSerializer.serialize(coinbaseTransaction)
12 |
13 | return HashBytes(hasher.hash(serialized))
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/masternodelist/MasternodeListMerkleRootCalculator.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.masternodelist
2 |
3 | import io.horizontalsystems.dashkit.models.Masternode
4 |
5 | class MasternodeListMerkleRootCalculator(val masternodeMerkleRootCreator: MerkleRootCreator) {
6 |
7 | fun calculateMerkleRoot(sortedMasternodes: List): ByteArray? {
8 | return masternodeMerkleRootCreator.create(sortedMasternodes.map { it.hash })
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/masternodelist/MerkleRootCreator.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.masternodelist
2 |
3 | import io.horizontalsystems.dashkit.IMerkleHasher
4 |
5 | class MerkleRootCreator(val hasher: IMerkleHasher) {
6 |
7 | fun create(hashes: List): ByteArray? {
8 | if (hashes.isEmpty()) return null
9 |
10 | var tmpHashes = hashes
11 |
12 | do {
13 | tmpHashes = joinHashes(tmpHashes)
14 | } while (tmpHashes.size > 1)
15 |
16 | return tmpHashes.first()
17 | }
18 |
19 | private fun joinHashes(hashes: List): List {
20 | val chunks = hashes.chunked(2)
21 |
22 | return chunks.map {
23 | hasher.hash(it.first(), it.last())
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/masternodelist/MerkleRootHasher.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.masternodelist
2 |
3 | import io.horizontalsystems.bitcoincore.core.IHasher
4 | import io.horizontalsystems.bitcoincore.utils.HashUtils
5 | import io.horizontalsystems.dashkit.IMerkleHasher
6 |
7 | class MerkleRootHasher: IHasher, IMerkleHasher {
8 |
9 | override fun hash(data: ByteArray): ByteArray {
10 | return HashUtils.doubleSha256(data)
11 | }
12 |
13 | override fun hash(first: ByteArray, second: ByteArray): ByteArray {
14 | return HashUtils.doubleSha256(first + second)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/masternodelist/QuorumListMerkleRootCalculator.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.masternodelist
2 |
3 | import io.horizontalsystems.dashkit.models.Quorum
4 |
5 | class QuorumListMerkleRootCalculator(private val merkleRootCreator: MerkleRootCreator) {
6 |
7 | fun calculateMerkleRoot(sortedQuorums: List): ByteArray? {
8 | return merkleRootCreator.create(sortedQuorums.map { it.hash })
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/messages/GetMasternodeListDiffMessage.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.messages
2 |
3 | import io.horizontalsystems.bitcoincore.extensions.toReversedHex
4 | import io.horizontalsystems.bitcoincore.io.BitcoinOutput
5 | import io.horizontalsystems.bitcoincore.network.messages.IMessage
6 | import io.horizontalsystems.bitcoincore.network.messages.IMessageSerializer
7 |
8 | class GetMasternodeListDiffMessage(val baseBlockHash: ByteArray, val blockHash: ByteArray) : IMessage {
9 | override fun toString(): String {
10 | return "GetMasternodeListDiffMessage(baseBlockHash=${baseBlockHash.toReversedHex()}, blockHash=${blockHash.toReversedHex()})"
11 | }
12 | }
13 |
14 | class GetMasternodeListDiffMessageSerializer : IMessageSerializer {
15 | override val command: String = "getmnlistd"
16 |
17 | override fun serialize(message: IMessage): ByteArray? {
18 | if (message !is GetMasternodeListDiffMessage) {
19 | return null
20 | }
21 |
22 | return BitcoinOutput()
23 | .write(message.baseBlockHash)
24 | .write(message.blockHash)
25 | .toByteArray()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/messages/TransactionLockMessage.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.messages
2 |
3 | import io.horizontalsystems.bitcoincore.extensions.toReversedHex
4 | import io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable
5 | import io.horizontalsystems.bitcoincore.network.messages.IMessage
6 | import io.horizontalsystems.bitcoincore.network.messages.IMessageParser
7 | import io.horizontalsystems.bitcoincore.serializers.TransactionSerializer
8 | import io.horizontalsystems.bitcoincore.storage.FullTransaction
9 |
10 | class TransactionLockMessage(var transaction: FullTransaction) : IMessage {
11 | override fun toString(): String {
12 | return "TransactionLockMessage(${transaction.header.hash.toReversedHex()})"
13 | }
14 | }
15 |
16 | class TransactionLockMessageParser : IMessageParser {
17 | override val command: String = "ix"
18 |
19 | override fun parseMessage(input: BitcoinInputMarkable): IMessage {
20 | val transaction = TransactionSerializer.deserialize(input)
21 | return TransactionLockMessage(transaction)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/CoinbaseTransaction.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.models
2 |
3 | import io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable
4 | import io.horizontalsystems.bitcoincore.serializers.TransactionSerializer
5 |
6 | class CoinbaseTransaction(input: BitcoinInputMarkable) {
7 | val transaction = TransactionSerializer.deserialize(input)
8 | val coinbaseTransactionSize: Long
9 | val version: Int
10 | val height: Long
11 | val merkleRootMNList: ByteArray
12 | val merkleRootQuorums: ByteArray?
13 | var bestCLHeightDiff: Long? = null
14 | var bestCLSignature: ByteArray? = null
15 | var creditPoolBalance: Long? = null
16 |
17 | init {
18 | coinbaseTransactionSize = input.readVarInt()
19 |
20 | version = input.readUnsignedShort()
21 | height = input.readUnsignedInt()
22 | merkleRootMNList = input.readBytes(32)
23 | merkleRootQuorums = when {
24 | version >= 2 -> input.readBytes(32)
25 | else -> null
26 | }
27 |
28 | if (version >= 3) {
29 | bestCLHeightDiff = input.readVarInt()
30 | bestCLSignature = input.readBytes(96)
31 | creditPoolBalance = input.readLong()
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/CoinbaseTransactionSerializer.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.models
2 |
3 | import io.horizontalsystems.bitcoincore.io.BitcoinOutput
4 | import io.horizontalsystems.bitcoincore.serializers.TransactionSerializer
5 |
6 | class CoinbaseTransactionSerializer {
7 |
8 | fun serialize(coinbaseTransaction: CoinbaseTransaction): ByteArray {
9 | val output = BitcoinOutput()
10 |
11 | output.write(TransactionSerializer.serialize(coinbaseTransaction.transaction))
12 | output.writeVarInt(coinbaseTransaction.coinbaseTransactionSize)
13 | output.writeUnsignedShort(coinbaseTransaction.version)
14 | output.writeUnsignedInt(coinbaseTransaction.height)
15 | output.write(coinbaseTransaction.merkleRootMNList)
16 |
17 | if (coinbaseTransaction.version >= 2) {
18 | output.write(coinbaseTransaction.merkleRootQuorums)
19 | }
20 |
21 | if (coinbaseTransaction.version >= 3) {
22 | coinbaseTransaction.bestCLHeightDiff?.let { output.writeVarInt(it) }
23 | coinbaseTransaction.bestCLSignature?.let { output.write(it) }
24 | coinbaseTransaction.creditPoolBalance?.let { output.writeLong(it) }
25 | }
26 |
27 | return output.toByteArray()
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/DashTransactionInfo.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.models
2 |
3 | import com.eclipsesource.json.Json
4 | import com.eclipsesource.json.JsonObject
5 | import io.horizontalsystems.bitcoincore.models.*
6 |
7 | class DashTransactionInfo : TransactionInfo {
8 |
9 | var instantTx: Boolean = false
10 |
11 | constructor(uid: String,
12 | transactionHash: String,
13 | transactionIndex: Int,
14 | inputs: List,
15 | outputs: List,
16 | amount: Long,
17 | type: TransactionType,
18 | fee: Long?,
19 | blockHeight: Int?,
20 | timestamp: Long,
21 | status: TransactionStatus,
22 | conflictingTxHash: String?,
23 | instantTx: Boolean
24 | ) : super(uid, transactionHash, transactionIndex, inputs, outputs, amount, type, fee, blockHeight, timestamp, status, conflictingTxHash) {
25 | this.instantTx = instantTx
26 | }
27 |
28 | @Throws
29 | constructor(serialized: String) : super(serialized) {
30 | val jsonObject = Json.parse(serialized).asObject()
31 | this.instantTx = jsonObject["instantTx"].asBoolean()
32 | }
33 |
34 | override fun asJsonObject(): JsonObject {
35 | val jsonObject = super.asJsonObject()
36 | jsonObject["instantTx"] = instantTx
37 | return jsonObject
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/InstantTransactionHash.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.models
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity
7 | data class InstantTransactionHash(@PrimaryKey val txHash: ByteArray) {
8 |
9 | override fun equals(other: Any?): Boolean {
10 | return other === this || other is InstantTransactionHash && txHash.contentEquals(other.txHash)
11 | }
12 |
13 | override fun hashCode(): Int {
14 | return txHash.contentHashCode()
15 | }
16 |
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/InstantTransactionInput.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.models
2 |
3 | import androidx.room.Entity
4 |
5 | @Entity(primaryKeys = ["txHash", "inputTxHash"])
6 | class InstantTransactionInput(
7 | val txHash: ByteArray,
8 | val inputTxHash: ByteArray,
9 | val timeCreated: Long,
10 | val voteCount: Int,
11 | val blockHeight: Int?)
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/InstantTransactionState.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.models
2 |
3 | class InstantTransactionState {
4 | var instantTransactionHashes = mutableListOf()
5 |
6 | fun append(hash: ByteArray) {
7 | instantTransactionHashes.add(hash)
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/MasternodeListState.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.models
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity
7 | class MasternodeListState(var baseBlockHash: ByteArray) {
8 |
9 | @PrimaryKey
10 | var primaryKey: String = "primary-key"
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/SpecialTransaction.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.models
2 |
3 | import io.horizontalsystems.bitcoincore.storage.FullTransaction
4 |
5 | class SpecialTransaction(
6 | val transaction: FullTransaction,
7 | extraPayload: ByteArray,
8 | forceHashUpdate: Boolean = true
9 | ): FullTransaction(transaction.header, transaction.inputs, transaction.outputs, forceHashUpdate)
10 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/tasks/PeerTaskFactory.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.tasks
2 |
3 | class PeerTaskFactory {
4 |
5 | fun createRequestMasternodeListDiffTask(baseBlockHash: ByteArray, blockHash: ByteArray): RequestMasternodeListDiffTask {
6 | return RequestMasternodeListDiffTask(baseBlockHash, blockHash)
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/tasks/RequestInstantSendLocksTask.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.tasks
2 |
3 | import io.horizontalsystems.bitcoincore.models.InventoryItem
4 | import io.horizontalsystems.bitcoincore.network.messages.GetDataMessage
5 | import io.horizontalsystems.bitcoincore.network.messages.IMessage
6 | import io.horizontalsystems.bitcoincore.network.peer.task.PeerTask
7 | import io.horizontalsystems.dashkit.InventoryType
8 | import io.horizontalsystems.dashkit.messages.ISLockMessage
9 |
10 | class RequestInstantSendLocksTask(hashes: List) : PeerTask() {
11 |
12 | val hashes = hashes.toMutableList()
13 | var isLocks = mutableListOf()
14 |
15 | override fun start() {
16 | requester?.send(GetDataMessage(hashes.map { InventoryItem(InventoryType.MSG_ISLOCK, it) }))
17 | }
18 |
19 | override fun handleMessage(message: IMessage) = when (message) {
20 | is ISLockMessage -> handleISLockVote(message)
21 | else -> false
22 | }
23 |
24 | private fun handleISLockVote(isLockMessage: ISLockMessage): Boolean {
25 | val hash = hashes.firstOrNull { it.contentEquals(isLockMessage.hash) } ?: return false
26 |
27 | hashes.remove(hash)
28 | isLocks.add(isLockMessage)
29 |
30 | if (hashes.isEmpty()) {
31 | listener?.onTaskCompleted(this)
32 | }
33 |
34 | return true
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/tasks/RequestMasternodeListDiffTask.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.tasks
2 |
3 | import io.horizontalsystems.bitcoincore.network.messages.IMessage
4 | import io.horizontalsystems.bitcoincore.network.peer.task.PeerTask
5 | import io.horizontalsystems.dashkit.messages.GetMasternodeListDiffMessage
6 | import io.horizontalsystems.dashkit.messages.MasternodeListDiffMessage
7 | import java.util.concurrent.TimeUnit
8 |
9 | class RequestMasternodeListDiffTask(private val baseBlockHash: ByteArray, private val blockHash: ByteArray) : PeerTask() {
10 |
11 | var masternodeListDiffMessage: MasternodeListDiffMessage? = null
12 |
13 | init {
14 | allowedIdleTime = TimeUnit.SECONDS.toMillis(5)
15 | }
16 |
17 | override fun handleTimeout() {
18 | listener?.onTaskFailed(this, Exception("RequestMasternodeListDiffTask Timeout"))
19 | }
20 |
21 |
22 | override fun start() {
23 | requester?.send(GetMasternodeListDiffMessage(baseBlockHash, blockHash))
24 | resetTimer()
25 | }
26 |
27 | override fun handleMessage(message: IMessage): Boolean {
28 | if (message is MasternodeListDiffMessage
29 | && message.baseBlockHash.contentEquals(baseBlockHash)
30 | && message.blockHash.contentEquals(blockHash)) {
31 |
32 | masternodeListDiffMessage = message
33 |
34 | listener?.onTaskCompleted(this)
35 |
36 | return true
37 | }
38 |
39 | return false
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/tasks/RequestTransactionLockRequestsTask.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.tasks
2 |
3 | import io.horizontalsystems.bitcoincore.models.InventoryItem
4 | import io.horizontalsystems.bitcoincore.network.messages.GetDataMessage
5 | import io.horizontalsystems.bitcoincore.network.messages.IMessage
6 | import io.horizontalsystems.bitcoincore.network.peer.task.PeerTask
7 | import io.horizontalsystems.bitcoincore.storage.FullTransaction
8 | import io.horizontalsystems.dashkit.InventoryType
9 | import io.horizontalsystems.dashkit.messages.TransactionLockMessage
10 |
11 | class RequestTransactionLockRequestsTask(hashes: List) : PeerTask() {
12 |
13 | val hashes = hashes.toMutableList()
14 | var transactions = mutableListOf()
15 |
16 | override fun start() {
17 | val items = hashes.map { hash ->
18 | InventoryItem(InventoryType.MSG_TXLOCK_REQUEST, hash)
19 | }
20 |
21 | requester?.send(GetDataMessage(items))
22 | }
23 |
24 | override fun handleMessage(message: IMessage) = when (message) {
25 | is TransactionLockMessage -> handleTransactionLockRequest(message.transaction)
26 | else -> false
27 | }
28 |
29 | private fun handleTransactionLockRequest(transaction: FullTransaction): Boolean {
30 | val hash = hashes.firstOrNull { it.contentEquals(transaction.header.hash) } ?: return false
31 |
32 | hashes.remove(hash)
33 | transactions.add(transaction)
34 |
35 | if (hashes.isEmpty()) {
36 | listener?.onTaskCompleted(this)
37 | }
38 |
39 | return true
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/tasks/RequestTransactionLockVotesTask.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.tasks
2 |
3 | import io.horizontalsystems.bitcoincore.models.InventoryItem
4 | import io.horizontalsystems.bitcoincore.network.messages.GetDataMessage
5 | import io.horizontalsystems.bitcoincore.network.messages.IMessage
6 | import io.horizontalsystems.bitcoincore.network.peer.task.PeerTask
7 | import io.horizontalsystems.dashkit.InventoryType
8 | import io.horizontalsystems.dashkit.messages.TransactionLockVoteMessage
9 |
10 | class RequestTransactionLockVotesTask(hashes: List) : PeerTask() {
11 |
12 | val hashes = hashes.toMutableList()
13 | var transactionLockVotes = mutableListOf()
14 |
15 | override fun start() {
16 | val items = hashes.map { hash ->
17 | InventoryItem(InventoryType.MSG_TXLOCK_VOTE, hash)
18 | }
19 |
20 | requester?.send(GetDataMessage(items))
21 | }
22 |
23 | override fun handleMessage(message: IMessage) = when (message) {
24 | is TransactionLockVoteMessage -> handleTransactionLockVote(message)
25 | else -> false
26 | }
27 |
28 | private fun handleTransactionLockVote(transactionLockVote: TransactionLockVoteMessage): Boolean {
29 | val hash = hashes.firstOrNull { it.contentEquals(transactionLockVote.hash) } ?: return false
30 |
31 | hashes.remove(hash)
32 | transactionLockVotes.add(transactionLockVote)
33 |
34 | if (hashes.isEmpty()) {
35 | listener?.onTaskCompleted(this)
36 | }
37 |
38 | return true
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/dashkit/src/main/kotlin/io/horizontalsystems/dashkit/validators/DarkGravityWaveTestnetValidator.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.validators
2 |
3 | import io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException
4 | import io.horizontalsystems.bitcoincore.blocks.validators.IBlockChainedValidator
5 | import io.horizontalsystems.bitcoincore.crypto.CompactBits
6 | import io.horizontalsystems.bitcoincore.models.Block
7 |
8 | class DarkGravityWaveTestnetValidator(
9 | private val targetSpacing: Int,
10 | private val targetTimespan: Long,
11 | private val maxTargetBits: Long,
12 | private val powDGWHeight: Int
13 | ) : IBlockChainedValidator {
14 |
15 | override fun validate(block: Block, previousBlock: Block) {
16 | if (block.timestamp > previousBlock.timestamp + 2 * targetTimespan) { // more than 2 cycles
17 | if (block.bits != maxTargetBits) {
18 | throw BlockValidatorException.NotEqualBits()
19 | }
20 |
21 | return
22 | }
23 |
24 | val blockTarget = CompactBits.decode(previousBlock.bits)
25 |
26 | var expectedBits = CompactBits.encode(blockTarget.multiply(10.toBigInteger()))
27 | if (expectedBits > maxTargetBits) {
28 | expectedBits = maxTargetBits
29 | }
30 |
31 | if (expectedBits != block.bits) {
32 | throw BlockValidatorException.NotEqualBits()
33 | }
34 | }
35 |
36 | override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {
37 | return block.height >= powDGWHeight && block.timestamp > previousBlock.timestamp + 4 * targetSpacing
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/dashkit/src/test/kotlin/io/horizontalsystems/dashkit/messages/MasternodeListDiffMessageParserTest.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.dashkit.messages
2 |
3 | import org.junit.jupiter.api.Assertions
4 | import org.spekframework.spek2.Spek
5 | import org.spekframework.spek2.style.specification.describe
6 | import java.io.File
7 |
8 | class MasternodeListDiffMessageParserTest : Spek({
9 | val messageParser = MasternodeListDiffMessageParser()
10 |
11 | describe("#parseMessage") {
12 | it("parses successfully") {
13 | val resource = javaClass.classLoader.getResource("messages/mnlistdiff.bin")
14 | val payload = File(resource.path).readBytes()
15 |
16 | Assertions.assertDoesNotThrow {
17 | messageParser.parseMessage(payload)
18 | }
19 | }
20 | }
21 | })
22 |
--------------------------------------------------------------------------------
/dashkit/src/test/resources/messages/mnlistdiff.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/horizontalsystems/bitcoin-kit-android/230e3625b6914e1ea6cd3801b77d6bc03cf1df92/dashkit/src/test/resources/messages/mnlistdiff.bin
--------------------------------------------------------------------------------
/dashkit/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker:
--------------------------------------------------------------------------------
1 | mock-maker-inline
--------------------------------------------------------------------------------
/ecashkit/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ecashkit/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ecashkit/src/main/kotlin/io/horizontalsystems/ecash/ECashRestoreKeyConverter.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.ecash
2 |
3 | import io.horizontalsystems.bitcoincore.core.scriptType
4 | import io.horizontalsystems.bitcoincore.extensions.toHexString
5 | import io.horizontalsystems.bitcoincore.managers.IRestoreKeyConverter
6 | import io.horizontalsystems.bitcoincore.models.PublicKey
7 | import io.horizontalsystems.bitcoincore.utils.IAddressConverter
8 | import io.horizontalsystems.hdwalletkit.HDWallet
9 |
10 | class ECashRestoreKeyConverter(
11 | private val addressConverter: IAddressConverter,
12 | private val purpose: HDWallet.Purpose
13 | ) : IRestoreKeyConverter {
14 | override fun keysForApiRestore(publicKey: PublicKey): List {
15 | return listOf(
16 | publicKey.publicKeyHash.toHexString(),
17 | addressConverter.convert(publicKey, purpose.scriptType).stringValue
18 | )
19 | }
20 |
21 | override fun bloomFilterElements(publicKey: PublicKey): List {
22 | return listOf(publicKey.publicKeyHash, publicKey.publicKey)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ecashkit/src/main/kotlin/io/horizontalsystems/ecash/MainNetECash.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.ecash
2 |
3 | import io.horizontalsystems.bitcoincore.network.Network
4 | import io.horizontalsystems.bitcoincore.transactions.scripts.Sighash
5 | import kotlin.experimental.or
6 |
7 | class MainNetECash : Network() {
8 |
9 | override var port: Int = 8333
10 |
11 | override var magic: Long = 0xe8f3e1e3L
12 | override var bip32HeaderPub: Int = 0x0488b21e
13 | override var bip32HeaderPriv: Int = 0x0488ade4
14 | override var addressVersion: Int = 0
15 | override var addressSegwitHrp: String = "ecash"
16 | override var addressScriptVersion: Int = 5
17 | override var coinType: Int = 899
18 | override val blockchairChainId: String = "ecash"
19 |
20 | override val maxBlockSize = 32 * 1024 * 1024
21 | override val dustRelayTxFee = 1000 // https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/src/policy/policy.h#L78
22 | override val sigHashForked = true
23 | override val sigHashValue = Sighash.FORKID or Sighash.ALL
24 |
25 | override var dnsSeeds = listOf(
26 | "x5.seed.bitcoinabc.org", // Bitcoin ABC seeder
27 | "btccash-seeder.bitcoinunlimited.info", // BU backed seeder
28 | "x5.seeder.jasonbcox.com", // Jason B. Cox
29 | "seed.deadalnix.me", // Amaury SÉCHET
30 | "seed.bchd.cash", // BCHD
31 | "x5.seeder.fabien.cash" // Fabien
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/ecashkit/src/test/java/io/horizontalsystems/ecash/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.ecash
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | android.defaults.buildfeatures.buildconfig=true
10 | android.enableJetifier=true
11 | android.nonFinalResIds=false
12 | android.nonTransitiveRClass=false
13 | android.useAndroidX=true
14 | org.gradle.jvmargs=-Xmx1536m
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/horizontalsystems/bitcoin-kit-android/230e3625b6914e1ea6cd3801b77d6bc03cf1df92/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Apr 09 17:20:26 KGT 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/hodler/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/hodler/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/horizontalsystems/bitcoin-kit-android/230e3625b6914e1ea6cd3801b77d6bc03cf1df92/hodler/consumer-rules.pro
--------------------------------------------------------------------------------
/hodler/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/hodler/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/hodler/src/main/kotlin/io/horizontalsystems/hodler/HodlerData.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.hodler
2 |
3 | import io.horizontalsystems.bitcoincore.core.IPluginData
4 |
5 | data class HodlerData(val lockTimeInterval: LockTimeInterval) : IPluginData
6 |
--------------------------------------------------------------------------------
/hodler/src/main/kotlin/io/horizontalsystems/hodler/HodlerOutputData.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.hodler
2 |
3 | import io.horizontalsystems.bitcoincore.core.IPluginOutputData
4 |
5 | class HodlerOutputData(val lockTimeInterval: LockTimeInterval,
6 | val addressString: String) : IPluginOutputData {
7 |
8 | var approxUnlockTime: Long? = null
9 |
10 | fun serialize(): String {
11 | return listOf(lockTimeInterval.serialize(), addressString).joinToString("|")
12 | }
13 |
14 | companion object {
15 | fun parse(serialized: String?): HodlerOutputData {
16 | val (lockTimeIntervalStr, addressString) = checkNotNull(serialized?.split("|"))
17 |
18 | val lockTimeInterval = checkNotNull(LockTimeInterval.deserialize(lockTimeIntervalStr))
19 | return HodlerOutputData(lockTimeInterval, addressString)
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/hodler/src/main/kotlin/io/horizontalsystems/hodler/LockTimeInterval.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.hodler
2 |
3 | import io.horizontalsystems.bitcoincore.utils.Utils
4 |
5 | enum class LockTimeInterval(private val value: Int) {
6 | hour(7),
7 | month(5063), // 30 * 24 * 60 * 60 / 512
8 | halfYear(30881), // 183 * 24 * 60 * 60 / 512
9 | year(61593); // 365 * 24 * 60 * 60 / 512
10 |
11 | private val sequenceTimeSecondsGranularity = 512
12 | private val relativeLockTimeLockMask = 0x400000 // (1 << 22)
13 |
14 | // need to write to extra data output as 2 bytes
15 | val valueAs2BytesLE: ByteArray = Utils.intToByteArray(value).reversedArray().copyOfRange(0, 2)
16 | val valueInSeconds: Int = value * sequenceTimeSecondsGranularity
17 |
18 | val sequenceNumber: Int = relativeLockTimeLockMask or value
19 | val sequenceNumberAs3BytesLE: ByteArray = Utils.intToByteArray(sequenceNumber).reversedArray().copyOfRange(0, 3)
20 |
21 | fun serialize(): String {
22 | return value.toString()
23 | }
24 |
25 | companion object {
26 | fun deserialize(serialized: String): LockTimeInterval? {
27 | return fromValue(serialized.toInt())
28 | }
29 |
30 | fun from2BytesLE(bytes: ByteArray): LockTimeInterval? {
31 | if (bytes.size != 2) return null
32 |
33 | return fromValue(Utils.byteArrayToUInt16LE(bytes))
34 | }
35 |
36 | private fun fromValue(value: Int): LockTimeInterval? {
37 | return values().find {
38 | it.value == value
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/hodler/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Hodler
3 |
4 |
--------------------------------------------------------------------------------
/hodler/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker:
--------------------------------------------------------------------------------
1 | mock-maker-inline
--------------------------------------------------------------------------------
/litecoinkit/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/litecoinkit/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/horizontalsystems/bitcoin-kit-android/230e3625b6914e1ea6cd3801b77d6bc03cf1df92/litecoinkit/consumer-rules.pro
--------------------------------------------------------------------------------
/litecoinkit/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/litecoinkit/src/androidTest/java/io/horizontalsystems/litecoinkit/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.litecoinkit
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("io.horizontalsystems.litecoinkit.test", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/litecoinkit/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/litecoinkit/src/main/kotlin/io/horizontalsystems/litecoinkit/MainNetLitecoin.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.litecoinkit
2 |
3 | import io.horizontalsystems.bitcoincore.network.Network
4 |
5 | class MainNetLitecoin : Network() {
6 | override val protocolVersion: Int = 70015
7 | override var port: Int = 9333
8 |
9 | override var magic: Long = 0xdbb6c0fb
10 | override var bip32HeaderPub: Int = 0x0488B21E // The 4 byte header that serializes in base58 to "xpub".
11 | override var bip32HeaderPriv: Int = 0x0488ADE4 // The 4 byte header that serializes in base58 to "xprv"
12 | override var addressVersion: Int = 0x30
13 | override var addressSegwitHrp: String = "ltc"
14 | override var addressScriptVersion: Int = 0x32
15 | override var coinType: Int = 2
16 | override val blockchairChainId: String = "litecoin"
17 |
18 | override val maxBlockSize = 1_000_000
19 | override val dustRelayTxFee = 3000 // https://github.com/bitcoin/bitcoin/blob/c536dfbcb00fb15963bf5d507b7017c241718bf6/src/policy/policy.h#L50
20 |
21 | override val syncableFromApi = true
22 |
23 | override var dnsSeeds = listOf(
24 | "seed-a.litecoin.loshan.co.uk",
25 | "x5.dnsseed.thrasher.io",
26 | "x5.dnsseed.litecointools.com",
27 | "x5.dnsseed.litecoinpool.org"
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/litecoinkit/src/main/kotlin/io/horizontalsystems/litecoinkit/ScryptHasher.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.litecoinkit
2 |
3 | import io.horizontalsystems.bitcoincore.core.IHasher
4 | import io.horizontalsystems.bitcoincore.utils.HashUtils
5 |
6 | class ScryptHasher : IHasher {
7 |
8 | override fun hash(data: ByteArray): ByteArray {
9 | return try {
10 | HashUtils.scrypt(data, data, 1024, 1, 1, 32).reversedArray()
11 | } catch (e: Exception) {
12 | byteArrayOf()
13 | }
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/litecoinkit/src/main/kotlin/io/horizontalsystems/litecoinkit/TestNetLitecoin.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.litecoinkit
2 |
3 | import io.horizontalsystems.bitcoincore.network.Network
4 |
5 | class TestNetLitecoin : Network() {
6 | override val protocolVersion: Int = 70015
7 | override var port: Int = 19335
8 |
9 | override var magic: Long = 0xf1c8d2fd
10 | override var bip32HeaderPub: Int = 0x043587CF
11 | override var bip32HeaderPriv: Int = 0x04358394
12 | override var addressVersion: Int = 111
13 | override var addressSegwitHrp: String = "tltc"
14 | override var addressScriptVersion: Int = 0x32
15 | override var coinType: Int = 1
16 | override val blockchairChainId: String = ""
17 |
18 | override val maxBlockSize = 1_000_000
19 | override val dustRelayTxFee = 3000 // https://github.com/bitcoin/bitcoin/blob/c536dfbcb00fb15963bf5d507b7017c241718bf6/src/policy/policy.h#L50
20 |
21 | override val syncableFromApi = false
22 |
23 | override var dnsSeeds = listOf(
24 | "testnet-seed.ltc.xurious.com",
25 | "seed-b.litecoin.loshan.co.uk",
26 | "dnsseed-testnet.thrasher.io"
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/litecoinkit/src/main/kotlin/io/horizontalsystems/litecoinkit/validators/ProofOfWorkValidator.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.litecoinkit.validators
2 |
3 | import io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException
4 | import io.horizontalsystems.bitcoincore.blocks.validators.IBlockChainedValidator
5 | import io.horizontalsystems.bitcoincore.crypto.CompactBits
6 | import io.horizontalsystems.bitcoincore.extensions.toHexString
7 | import io.horizontalsystems.bitcoincore.io.BitcoinOutput
8 | import io.horizontalsystems.bitcoincore.models.Block
9 | import io.horizontalsystems.litecoinkit.ScryptHasher
10 | import java.math.BigInteger
11 |
12 | class ProofOfWorkValidator(private val scryptHasher: ScryptHasher) : IBlockChainedValidator {
13 |
14 | override fun validate(block: Block, previousBlock: Block) {
15 | val blockHeaderData = getSerializedBlockHeader(block)
16 |
17 | val powHash = scryptHasher.hash(blockHeaderData).toHexString()
18 |
19 | check(BigInteger(powHash, 16) < CompactBits.decode(block.bits)) {
20 | throw BlockValidatorException.InvalidProofOfWork()
21 | }
22 | }
23 |
24 | private fun getSerializedBlockHeader(block: Block): ByteArray {
25 | return BitcoinOutput()
26 | .writeInt(block.version)
27 | .write(block.previousBlockHash)
28 | .write(block.merkleRoot)
29 | .writeUnsignedInt(block.timestamp)
30 | .writeUnsignedInt(block.bits)
31 | .writeUnsignedInt(block.nonce)
32 | .toByteArray()
33 | }
34 |
35 | override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {
36 | return true
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/litecoinkit/src/main/resources/MainNetLitecoin-bip44.checkpoint:
--------------------------------------------------------------------------------
1 | 02000000982f9bc89bf1a828f3472ec5b332b40c3e904336fdce82d39e881108f3106a639fec4363378ef3ace42b11af273bed070d6459f00025b249c1b448cd7e0486997b1f5853037a0c1b39bd90dda07508009f742a26e3a5c6deb6bbb7f73fd88ea21473ba6600bdb1863ead4f15e5056a25
2 | 02000000a75c1531c307b047c759bc15b9988acb9b02d4c8d3071633de78dc98a373c47940eaaf37543281e9d64209d23f997313d218b1607bf2416e4cd42b6337f8ec17cd1e5853d0b10c1b069a92349f750800982f9bc89bf1a828f3472ec5b332b40c3e904336fdce82d39e881108f3106a63
3 |
--------------------------------------------------------------------------------
/litecoinkit/src/main/resources/MainNetLitecoin.checkpoint:
--------------------------------------------------------------------------------
1 | 000000208eddc2d868de32727ef1cbe79c2370f7fdc4e42fe22d03c7dc889f535ead297e6651ce86b09d673e3085a48106abcc74b8c65f75ac94495a9b8425346ab2348f64fdba67b5c73419ad21e72a407f2b00d25f02b2f9ea50995d6db931f40c2efc4601084f26b3e16317ecaf6c6ff55e11
2 | 00000020f1303e96cbf45ad97da7bc408e63b10af86fc4bef342776d80d06e3630a645aa4b8b3e7192a9414b81ee35313fe72ddb275a516e8d6a7ecdbf65574be5e287c346fcba67ad3e381955c44fc53f7f2b008eddc2d868de32727ef1cbe79c2370f7fdc4e42fe22d03c7dc889f535ead297e
3 |
--------------------------------------------------------------------------------
/litecoinkit/src/main/resources/TestNetLitecoin-bip44.checkpoint:
--------------------------------------------------------------------------------
1 | 03000020e5139533e5d19576f69c1ba6c2a3dcdb2f25d38a03b7b181ad9b9e463439080adddcf013d87d13dc5a945b7f3ccc7f35aff69b0164e0313e4b24257d0c90cb8a264ea2580257041e0006e356e007000015c0dcbb2e6b9280f7f93daff368f7b2243a3d04b3c18da574b103d500459a5f
2 | 00000020cf8fdf87e046e6320f77d07f0ee1bfc289d524691af67373e7e5a23a68c5f9c573b4c778d63094be9b8ab63bd958705b4814702deabb97ac409cc71b4bde086d5f4ca258f0ff0f1e000b5ac7df070000e5139533e5d19576f69c1ba6c2a3dcdb2f25d38a03b7b181ad9b9e463439080a
3 |
--------------------------------------------------------------------------------
/litecoinkit/src/main/resources/TestNetLitecoin.checkpoint:
--------------------------------------------------------------------------------
1 | 000000204f6f64abec2fd95e42c06a8fcfcc4d548e9ef92519ad0e80b0366342958e943dbd8cfbf0e31f74308de69e636ef828f35b323614242a46ef130f86f1be212f09cb393a63c4830d1e95360000c07b24006aff45116256131ad94b2870374284d65e10ea5d6889d9cbc7facd092df9013a
2 |
--------------------------------------------------------------------------------
/litecoinkit/src/test/java/io/horizontalsystems/litecoinkit/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.litecoinkit
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', // Demo app
2 | ':bitcoincore', // Bitcoin Core
3 | ':bitcoinkit', // Bitcoin kit
4 | ':dashkit', // Dash kit
5 | ':bitcoincashkit', // Bitcoin Cash kit
6 | ':ecashkit', // Bitcoin Cash kit
7 | ':litecoinkit', // Litecoin kit
8 | ':hodler', // Hodler
9 | ':tools' // Checkpoint syncer
10 |
--------------------------------------------------------------------------------
/tools/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/tools/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-kapt'
4 |
5 | android {
6 | compileSdkVersion 34
7 |
8 | defaultConfig {
9 | minSdkVersion 23
10 | targetSdkVersion 34
11 |
12 | consumerProguardFiles 'consumer-rules.pro'
13 | }
14 |
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | compileOptions {
22 | sourceCompatibility JavaVersion.VERSION_17
23 | targetCompatibility JavaVersion.VERSION_17
24 | }
25 | namespace 'io.horizontalsystems.tools'
26 | }
27 |
28 | dependencies {
29 | implementation fileTree(dir: 'libs', include: ['*.jar'])
30 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
31 |
32 | implementation project(':bitcoincore')
33 | implementation project(':bitcoinkit')
34 | implementation project(':dashkit')
35 | implementation project(':bitcoincashkit')
36 | implementation project(':litecoinkit')
37 | implementation project(':ecashkit')
38 | }
39 |
--------------------------------------------------------------------------------
/tools/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/horizontalsystems/bitcoin-kit-android/230e3625b6914e1ea6cd3801b77d6bc03cf1df92/tools/consumer-rules.pro
--------------------------------------------------------------------------------
/tools/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/tools/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tools/src/main/java/io/horizontalsystems/tools/Tools.kt:
--------------------------------------------------------------------------------
1 | package io.horizontalsystems.tools
2 |
3 | import io.horizontalsystems.bitcoincore.models.Block
4 | import io.horizontalsystems.bitcoincore.storage.BlockHeader
5 | import io.horizontalsystems.bitcoincore.utils.HashUtils
6 | import java.util.logging.Level
7 | import java.util.logging.Logger
8 |
9 | // Go to
10 | // Edit Configurations... -> ToolsKt -> VM Options
11 | // And paste the following
12 | // -classpath $Classpath$:bitcoincashkit/src/main/resources:bitcoinkit/src/main/resources:dashkit/src/main/resources:ecashkit/src/main/resources:litecoinkit/src/main/resources
13 | fun main() {
14 | Logger.getLogger("").level = Level.SEVERE
15 | syncCheckpoints()
16 | }
17 |
18 | private fun syncCheckpoints() {
19 | BuildCheckpoints().sync()
20 | Thread.sleep(5000)
21 | }
22 |
23 | private fun buildCustomCheckpoint() {
24 | val checkpointBlock = Block(BlockHeader(
25 | version = 2,
26 | previousBlockHeaderHash = HashUtils.toBytesAsLE("00000000000000006bcf448b771c8f4db4e2ca653474e3b29504ec08422b3fba"),
27 | merkleRoot = HashUtils.toBytesAsLE("4ea18e999a57fc55fb390558dbb88a7b9c55c71c7de4cec160c045802ee587d2"),
28 | timestamp = 1397755646,
29 | bits = 419470732,
30 | nonce = 2160181286,
31 | hash = HashUtils.toBytesAsLE("00000000000000003decdbb5f3811eab3148fbc29d3610528eb3b50d9ee5723f")
32 | ), 296352)
33 |
34 | BuildCheckpoints().build(checkpointBlock)
35 | }
36 |
--------------------------------------------------------------------------------
/tools/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | tools
3 |
4 |
--------------------------------------------------------------------------------