From ad6c2563bd982339b323dd8b1fbd3ffdabebd4b3 Mon Sep 17 00:00:00 2001 From: Ben Carman Date: Wed, 30 Sep 2020 08:50:49 -0500 Subject: [PATCH] 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 --- .../server/BitcoindRpcBackendUtil.scala | 89 +++++++++++++++++++ .../main/scala/org/bitcoins/server/Main.scala | 8 +- .../rpc/client/common/BitcoindRpcClient.scala | 4 +- .../org/bitcoins/testkit/chain/SyncUtil.scala | 47 +--------- .../testkit/wallet/BitcoinSWalletTest.scala | 2 +- 5 files changed, 95 insertions(+), 55 deletions(-) create mode 100644 app/server/src/main/scala/org/bitcoins/server/BitcoindRpcBackendUtil.scala diff --git a/app/server/src/main/scala/org/bitcoins/server/BitcoindRpcBackendUtil.scala b/app/server/src/main/scala/org/bitcoins/server/BitcoindRpcBackendUtil.scala new file mode 100644 index 0000000000..57e0bfcf98 --- /dev/null +++ b/app/server/src/main/scala/org/bitcoins/server/BitcoindRpcBackendUtil.scala @@ -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(_ => ()) + } + } + } +} diff --git a/app/server/src/main/scala/org/bitcoins/server/Main.scala b/app/server/src/main/scala/org/bitcoins/server/Main.scala index 94f5edd665..42df62d33e 100644 --- a/app/server/src/main/scala/org/bitcoins/server/Main.scala +++ b/app/server/src/main/scala/org/bitcoins/server/Main.scala @@ -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 diff --git a/bitcoind-rpc/src/main/scala/org/bitcoins/rpc/client/common/BitcoindRpcClient.scala b/bitcoind-rpc/src/main/scala/org/bitcoins/rpc/client/common/BitcoindRpcClient.scala index 6dd79a06d4..b152f8e94f 100644 --- a/bitcoind-rpc/src/main/scala/org/bitcoins/rpc/client/common/BitcoindRpcClient.scala +++ b/bitcoind-rpc/src/main/scala/org/bitcoins/rpc/client/common/BitcoindRpcClient.scala @@ -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 diff --git a/testkit/src/main/scala/org/bitcoins/testkit/chain/SyncUtil.scala b/testkit/src/main/scala/org/bitcoins/testkit/chain/SyncUtil.scala index 5f0edf3c85..ca5649d428 100644 --- a/testkit/src/main/scala/org/bitcoins/testkit/chain/SyncUtil.scala +++ b/testkit/src/main/scala/org/bitcoins/testkit/chain/SyncUtil.scala @@ -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} @@ -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 { @@ -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) } @@ -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) diff --git a/testkit/src/main/scala/org/bitcoins/testkit/wallet/BitcoinSWalletTest.scala b/testkit/src/main/scala/org/bitcoins/testkit/wallet/BitcoinSWalletTest.scala index 56a1a0d095..44db7a6d1e 100644 --- a/testkit/src/main/scala/org/bitcoins/testkit/wallet/BitcoinSWalletTest.scala +++ b/testkit/src/main/scala/org/bitcoins/testkit/wallet/BitcoinSWalletTest.scala @@ -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)