Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Create Util functions for wallets with a bitcoind backend (#2076)
* Create Util functions for wallets with a bitcoind backend

* Move function, delete reduant function

* Fix imports
  • Loading branch information
benthecarman committed Sep 30, 2020
1 parent 23d0d7e commit c853151
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 55 deletions.
@@ -0,0 +1,89 @@
package org.bitcoins.server

import org.bitcoins.core.api.node.NodeApi
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.util.{BitcoinSLogger, FutureUtil}
import org.bitcoins.crypto.DoubleSha256Digest
import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.wallet.Wallet

import scala.concurrent.{ExecutionContext, Future, Promise}

/** Useful utilities to use in the wallet project for syncing things against bitcoind */
object BitcoindRpcBackendUtil extends BitcoinSLogger {

def createWalletWithBitcoindCallbacks(
bitcoind: BitcoindRpcClient,
wallet: Wallet)(implicit ec: ExecutionContext): Wallet = {
// Kill the old wallet
wallet.stopWalletThread()

// We need to create a promise so we can inject the wallet with the callback
// after we have created it into SyncUtil.getNodeApiWalletCallback
// so we don't lose the internal state of the wallet
val walletCallbackP = Promise[Wallet]()

val pairedWallet = Wallet(
keyManager = wallet.keyManager,
nodeApi =
BitcoindRpcBackendUtil.getNodeApiWalletCallback(bitcoind,
walletCallbackP.future),
chainQueryApi = bitcoind,
feeRateApi = wallet.feeRateApi,
creationTime = wallet.keyManager.creationTime
)(wallet.walletConfig, wallet.ec)

walletCallbackP.success(pairedWallet)

pairedWallet
}

def getNodeApiWalletCallback(
bitcoindRpcClient: BitcoindRpcClient,
walletF: Future[Wallet])(implicit ec: ExecutionContext): NodeApi = {
new NodeApi {

override def downloadBlocks(
blockHashes: Vector[DoubleSha256Digest]): Future[Unit] = {
logger.info(s"Fetching ${blockHashes.length} hashes from bitcoind")
val f: Vector[DoubleSha256Digest] => Future[Wallet] = { hashes =>
val blocksF =
FutureUtil.sequentially(hashes)(bitcoindRpcClient.getBlockRaw)

val updatedWalletF = for {
blocks <- blocksF
wallet <- walletF
processedWallet <- {
FutureUtil.foldLeftAsync(wallet, blocks) {
case (wallet, block) =>
wallet.processBlock(block)
}
}
} yield processedWallet

updatedWalletF
}

val batchSize = 25
val batchedExecutedF = FutureUtil.batchExecute(elements = blockHashes,
f = f,
init = Vector.empty,
batchSize = batchSize)

batchedExecutedF.map { _ =>
logger.info(
s"Done fetching ${blockHashes.length} hashes from bitcoind")
()
}
}

/**
* Broadcasts the given transaction over the P2P network
*/
override def broadcastTransaction(
transaction: Transaction): Future[Unit] = {
bitcoindRpcClient.sendRawTransaction(transaction).map(_ => ())
}
}
}
}
8 changes: 1 addition & 7 deletions app/server/src/main/scala/org/bitcoins/server/Main.scala
Expand Up @@ -15,13 +15,7 @@ import org.bitcoins.chain.models.{
}
import org.bitcoins.core.Core
import org.bitcoins.core.api.chain.ChainApi
import org.bitcoins.core.config.{
BitcoinNetworks,
MainNet,
RegTest,
SigNet,
TestNet3
}
import org.bitcoins.core.config._
import org.bitcoins.core.util.{FutureUtil, NetworkUtil}
import org.bitcoins.db._
import org.bitcoins.feeprovider.BitcoinerLiveFeeRateProvider
Expand Down
Expand Up @@ -103,9 +103,7 @@ class BitcoindRpcClient(val instance: BitcoindInstance)(implicit

/** Gets the block height of the closest block to the given time */
override def epochSecondToBlockHeight(time: Long): Future[Int] =
Future.failed(
new UnsupportedOperationException(
"epochSecondToBlockHeight is not supported by bitcoind"))
Future.successful(0)

// Node Api

Expand Down
47 changes: 3 additions & 44 deletions testkit/src/main/scala/org/bitcoins/testkit/chain/SyncUtil.scala
Expand Up @@ -2,12 +2,9 @@ package org.bitcoins.testkit.chain

import org.bitcoins.chain.blockchain.sync.FilterWithHeaderHash
import org.bitcoins.commons.jsonmodels.bitcoind.GetBlockFilterResult
import org.bitcoins.core.api.chain.ChainQueryApi.FilterResponse
import org.bitcoins.core.api.node.{NodeApi, NodeChainQueryApi}
import org.bitcoins.core.api.chain.ChainQueryApi
import org.bitcoins.core.api.node
import org.bitcoins.core.api.node.{NodeApi, NodeChainQueryApi}
import org.bitcoins.core.gcs.FilterType
import org.bitcoins.core.protocol.BlockStamp
import org.bitcoins.core.protocol.blockchain.BlockHeader
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.util.{BitcoinSLogger, FutureUtil}
Expand Down Expand Up @@ -48,44 +45,6 @@ abstract class SyncUtil extends BitcoinSLogger {
}
}

def getTestChainQueryApi(bitcoind: BitcoindRpcClient): ChainQueryApi = {
new ChainQueryApi {

/** Gets the height of the given block */
override def getBlockHeight(
blockHash: DoubleSha256DigestBE): Future[Option[Int]] =
bitcoind.getBlockHeight(blockHash)

/** Gets the hash of the block that is what we consider "best" */
override def getBestBlockHash(): Future[DoubleSha256DigestBE] = {
bitcoind.getBestBlockHash
}

/** Gets number of confirmations for the given block hash */
override def getNumberOfConfirmations(
blockHashOpt: DoubleSha256DigestBE): Future[Option[Int]] = {
bitcoind.getNumberOfConfirmations(blockHashOpt)
}

/** Gets the number of compact filters in the database */
override def getFilterCount: Future[Int] = {
bitcoind.getFilterCount
}

/** Returns the block height of the given block stamp */
override def getHeightByBlockStamp(blockStamp: BlockStamp): Future[Int] =
bitcoind.getHeightByBlockStamp(blockStamp)

override def epochSecondToBlockHeight(time: Long): Future[Int] =
Future.successful(0)

override def getFiltersBetweenHeights(
startHeight: Int,
endHeight: Int): Future[Vector[FilterResponse]] =
bitcoind.getFiltersBetweenHeights(startHeight, endHeight)
}
}

def getNodeApi(bitcoindRpcClient: BitcoindRpcClient)(implicit
ec: ExecutionContext): NodeApi = {
new NodeApi {
Expand Down Expand Up @@ -180,7 +139,7 @@ abstract class SyncUtil extends BitcoinSLogger {

def getNodeChainQueryApi(bitcoind: BitcoindRpcClient)(implicit
ec: ExecutionContext): NodeChainQueryApi = {
val chainQuery = SyncUtil.getTestChainQueryApi(bitcoind)
val chainQuery = bitcoind
val nodeApi = SyncUtil.getNodeApi(bitcoind)
node.NodeChainQueryApi(nodeApi, chainQuery)
}
Expand All @@ -189,7 +148,7 @@ abstract class SyncUtil extends BitcoinSLogger {
bitcoind: BitcoindRpcClient,
walletF: Future[Wallet])(implicit
ec: ExecutionContext): NodeChainQueryApi = {
val chainQuery = SyncUtil.getTestChainQueryApi(bitcoind)
val chainQuery = bitcoind
val nodeApi =
SyncUtil.getNodeApiWalletCallback(bitcoind, walletF)
node.NodeChainQueryApi(nodeApi, chainQuery)
Expand Down
Expand Up @@ -474,7 +474,7 @@ object BitcoinSWalletTest extends WalletLogger {
keyManager = wallet.keyManager,
nodeApi =
SyncUtil.getNodeApiWalletCallback(bitcoind, walletCallbackP.future),
chainQueryApi = SyncUtil.getTestChainQueryApi(bitcoind),
chainQueryApi = bitcoind,
feeRateApi = new RandomFeeProvider,
creationTime = wallet.keyManager.creationTime
)(wallet.walletConfig, wallet.ec)
Expand Down

0 comments on commit c853151

Please sign in to comment.