mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-15 20:30:17 +01:00
[Neutrino] Update balances (#888)
* [Neutrino] Update balances * responded to the comments * some more changes
This commit is contained in:
parent
29e0c9cd6a
commit
2599a1abfa
7 changed files with 182 additions and 19 deletions
|
@ -77,12 +77,17 @@ object Main extends App {
|
||||||
|
|
||||||
val callbacks = {
|
val callbacks = {
|
||||||
import DataMessageHandler._
|
import DataMessageHandler._
|
||||||
val onTX: OnTxReceived = { tx =>
|
val onTx: OnTxReceived = { tx =>
|
||||||
wallet.processTransaction(tx, confirmations = 0)
|
wallet.processTransaction(tx, confirmations = 0)
|
||||||
()
|
()
|
||||||
}
|
}
|
||||||
|
val onBlock: OnBlockReceived = { block =>
|
||||||
|
wallet.processBlock(block, 0)
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
SpvNodeCallbacks(onTxReceived = Seq(onTX))
|
SpvNodeCallbacks(onTxReceived = Seq(onTx),
|
||||||
|
onBlockReceived = Seq(onBlock))
|
||||||
}
|
}
|
||||||
if (nodeConf.isSPVEnabled) {
|
if (nodeConf.isSPVEnabled) {
|
||||||
for {
|
for {
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
package org.bitcoins.node
|
||||||
|
|
||||||
|
import akka.actor.Cancellable
|
||||||
|
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
||||||
|
import org.bitcoins.core.currency._
|
||||||
|
import org.bitcoins.node.networking.peer.DataMessageHandler
|
||||||
|
import org.bitcoins.rpc.client.common.BitcoindVersion
|
||||||
|
import org.bitcoins.rpc.util.AsyncUtil
|
||||||
|
import org.bitcoins.server.BitcoinSAppConfig
|
||||||
|
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||||
|
import org.bitcoins.testkit.fixtures.UsesExperimentalBitcoind
|
||||||
|
import org.bitcoins.testkit.node.NodeUnitTest.NeutrinoNodeFundedWalletBitcoind
|
||||||
|
import org.bitcoins.testkit.node.{NodeTestUtil, NodeUnitTest}
|
||||||
|
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||||
|
import org.bitcoins.wallet.api.UnlockedWalletApi
|
||||||
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
import org.bitcoins.wallet.models.SpendingInfoTable
|
||||||
|
import org.scalatest.exceptions.TestFailedException
|
||||||
|
import org.scalatest.{DoNotDiscover, FutureOutcome}
|
||||||
|
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
import scala.concurrent.{Future, Promise}
|
||||||
|
|
||||||
|
@DoNotDiscover
|
||||||
|
class NeutrinoNodeWithWalletTest extends NodeUnitTest {
|
||||||
|
|
||||||
|
/** Wallet config with data directory set to user temp directory */
|
||||||
|
implicit override protected def config: BitcoinSAppConfig =
|
||||||
|
BitcoinSTestAppConfig.getNeutrinoTestConfig()
|
||||||
|
|
||||||
|
override type FixtureParam = NeutrinoNodeFundedWalletBitcoind
|
||||||
|
|
||||||
|
def withFixture(test: OneArgAsyncTest): FutureOutcome = {
|
||||||
|
withNeutrinoNodeFundedWalletBitcoind(test,
|
||||||
|
callbacks,
|
||||||
|
Some(BitcoindVersion.Experimental))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val assertionP: Promise[Boolean] = Promise()
|
||||||
|
|
||||||
|
private val walletP: Promise[UnlockedWalletApi] = Promise()
|
||||||
|
private val walletF: Future[UnlockedWalletApi] = walletP.future
|
||||||
|
|
||||||
|
val amountFromBitcoind = 1.bitcoin
|
||||||
|
|
||||||
|
def callbacks: SpvNodeCallbacks = {
|
||||||
|
val onBlock: DataMessageHandler.OnBlockReceived = { block =>
|
||||||
|
for {
|
||||||
|
wallet <- walletF
|
||||||
|
_ <- wallet.processBlock(block, confirmations = 0)
|
||||||
|
} yield ()
|
||||||
|
}
|
||||||
|
SpvNodeCallbacks(
|
||||||
|
onBlockReceived = Seq(onBlock)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "receive information about received payments" taggedAs (UsesExperimentalBitcoind) in {
|
||||||
|
param =>
|
||||||
|
val NeutrinoNodeFundedWalletBitcoind(node, wallet, bitcoind) = param
|
||||||
|
|
||||||
|
walletP.success(wallet)
|
||||||
|
|
||||||
|
def clearSpendingInfoTable(): Future[Int] = {
|
||||||
|
import slick.jdbc.SQLiteProfile.api._
|
||||||
|
|
||||||
|
val conf: WalletAppConfig = wallet.walletConfig
|
||||||
|
val table = TableQuery[SpendingInfoTable]
|
||||||
|
conf.database.run(table.delete)
|
||||||
|
}
|
||||||
|
|
||||||
|
def condition(): Future[Boolean] = {
|
||||||
|
for {
|
||||||
|
balance <- wallet.getUnconfirmedBalance()
|
||||||
|
addresses <- wallet.listAddresses()
|
||||||
|
utxos <- wallet.listUtxos()
|
||||||
|
} yield {
|
||||||
|
balance == BitcoinSWalletTest.initialFunds + amountFromBitcoind &&
|
||||||
|
utxos.size == 2 &&
|
||||||
|
addresses.map(_.scriptPubKey) == utxos.map(_.output.scriptPubKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
addresses <- wallet.listAddresses()
|
||||||
|
utxos <- wallet.listUtxos()
|
||||||
|
_ = assert(addresses.size == 1)
|
||||||
|
_ = assert(utxos.size == 1)
|
||||||
|
|
||||||
|
_ <- node.sync()
|
||||||
|
_ <- NodeTestUtil.awaitSync(node, bitcoind)
|
||||||
|
_ <- NodeTestUtil.awaitCompactFiltersSync(node, bitcoind)
|
||||||
|
|
||||||
|
address <- wallet.getNewAddress()
|
||||||
|
_ <- bitcoind
|
||||||
|
.sendToAddress(address, amountFromBitcoind)
|
||||||
|
|
||||||
|
addresses <- wallet.listAddresses()
|
||||||
|
utxos <- wallet.listUtxos()
|
||||||
|
_ = assert(addresses.size == 2)
|
||||||
|
_ = assert(utxos.size == 1)
|
||||||
|
|
||||||
|
_ <- clearSpendingInfoTable()
|
||||||
|
|
||||||
|
addresses <- wallet.listAddresses()
|
||||||
|
utxos <- wallet.listUtxos()
|
||||||
|
_ = assert(addresses.size == 2)
|
||||||
|
_ = assert(utxos.size == 0)
|
||||||
|
|
||||||
|
_ <- bitcoind.getNewAddress
|
||||||
|
.flatMap(bitcoind.generateToAddress(1, _))
|
||||||
|
_ <- NodeTestUtil.awaitSync(node, bitcoind)
|
||||||
|
_ <- NodeTestUtil.awaitCompactFiltersSync(node, bitcoind)
|
||||||
|
|
||||||
|
addresses <- wallet.listAddresses()
|
||||||
|
_ <- node.rescan(addresses.map(_.scriptPubKey))
|
||||||
|
|
||||||
|
_ <- AsyncUtil.awaitConditionF(condition)
|
||||||
|
} yield succeed
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ import org.scalatest.exceptions.TestFailedException
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.{Future, Promise}
|
import scala.concurrent.{Future, Promise}
|
||||||
|
|
||||||
class NodeWithWalletTest extends NodeUnitTest {
|
class SpvNodeWithWalletTest extends NodeUnitTest {
|
||||||
|
|
||||||
/** Wallet config with data directory set to user temp directory */
|
/** Wallet config with data directory set to user temp directory */
|
||||||
implicit override protected def config: BitcoinSAppConfig =
|
implicit override protected def config: BitcoinSAppConfig =
|
|
@ -121,18 +121,22 @@ abstract class NodeTestUtil extends P2PLogger {
|
||||||
implicit ec: ExecutionContext): Future[Boolean] = {
|
implicit ec: ExecutionContext): Future[Boolean] = {
|
||||||
val rpcCountF = rpc.getBlockCount
|
val rpcCountF = rpc.getBlockCount
|
||||||
for {
|
for {
|
||||||
count <- node.chainApiFromDb().flatMap(_.getFilterCount)
|
filterCount <- node.chainApiFromDb().flatMap(_.getFilterCount)
|
||||||
rpcCount <- rpcCountF
|
blockCount <- rpcCountF
|
||||||
} yield rpcCount == count
|
} yield {
|
||||||
|
blockCount == filterCount
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def isSameBestFilterHeaderHeight(node: NeutrinoNode, rpc: BitcoindRpcClient)(
|
def isSameBestFilterHeaderHeight(node: NeutrinoNode, rpc: BitcoindRpcClient)(
|
||||||
implicit ec: ExecutionContext): Future[Boolean] = {
|
implicit ec: ExecutionContext): Future[Boolean] = {
|
||||||
val rpcCountF = rpc.getBlockCount
|
val rpcCountF = rpc.getBlockCount
|
||||||
for {
|
for {
|
||||||
count <- node.chainApiFromDb().flatMap(_.getFilterHeaderCount)
|
filterHeaderCount <- node.chainApiFromDb().flatMap(_.getFilterHeaderCount)
|
||||||
rpcCount <- rpcCountF
|
blockCount <- rpcCountF
|
||||||
} yield rpcCount == count
|
} yield {
|
||||||
|
blockCount == filterHeaderCount
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Checks if the given light client and bitcoind
|
/** Checks if the given light client and bitcoind
|
||||||
|
|
|
@ -83,6 +83,8 @@ trait BitcoinSWalletTest extends BitcoinSFixture with WalletLogger {
|
||||||
|
|
||||||
object BitcoinSWalletTest extends WalletLogger {
|
object BitcoinSWalletTest extends WalletLogger {
|
||||||
|
|
||||||
|
lazy val initialFunds = 25.bitcoins
|
||||||
|
|
||||||
case class WalletWithBitcoind(
|
case class WalletWithBitcoind(
|
||||||
wallet: UnlockedWalletApi,
|
wallet: UnlockedWalletApi,
|
||||||
bitcoind: BitcoindRpcClient)
|
bitcoind: BitcoindRpcClient)
|
||||||
|
@ -173,7 +175,7 @@ object BitcoinSWalletTest extends WalletLogger {
|
||||||
for {
|
for {
|
||||||
addr <- wallet.getNewAddress()
|
addr <- wallet.getNewAddress()
|
||||||
tx <- bitcoind
|
tx <- bitcoind
|
||||||
.sendToAddress(addr, 25.bitcoins)
|
.sendToAddress(addr, initialFunds)
|
||||||
.flatMap(bitcoind.getRawTransaction(_))
|
.flatMap(bitcoind.getRawTransaction(_))
|
||||||
|
|
||||||
_ <- bitcoind.getNewAddress.flatMap(bitcoind.generateToAddress(6, _))
|
_ <- bitcoind.getNewAddress.flatMap(bitcoind.generateToAddress(6, _))
|
||||||
|
@ -181,7 +183,7 @@ object BitcoinSWalletTest extends WalletLogger {
|
||||||
balance <- wallet.getBalance()
|
balance <- wallet.getBalance()
|
||||||
|
|
||||||
} yield {
|
} yield {
|
||||||
assert(balance >= 25.bitcoins)
|
assert(balance >= initialFunds)
|
||||||
pair
|
pair
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import org.bitcoins.core.crypto._
|
||||||
import org.bitcoins.core.currency.CurrencyUnit
|
import org.bitcoins.core.currency.CurrencyUnit
|
||||||
import org.bitcoins.core.hd.{AddressType, HDPurpose}
|
import org.bitcoins.core.hd.{AddressType, HDPurpose}
|
||||||
import org.bitcoins.core.protocol.BitcoinAddress
|
import org.bitcoins.core.protocol.BitcoinAddress
|
||||||
import org.bitcoins.core.protocol.blockchain.ChainParams
|
import org.bitcoins.core.protocol.blockchain.{Block, ChainParams}
|
||||||
import org.bitcoins.core.protocol.transaction.Transaction
|
import org.bitcoins.core.protocol.transaction.Transaction
|
||||||
import org.bitcoins.core.wallet.fee.FeeUnit
|
import org.bitcoins.core.wallet.fee.FeeUnit
|
||||||
import org.bitcoins.wallet.HDUtil
|
import org.bitcoins.wallet.HDUtil
|
||||||
|
@ -52,6 +52,12 @@ trait LockedWalletApi extends WalletApi {
|
||||||
transaction: Transaction,
|
transaction: Transaction,
|
||||||
confirmations: Int): Future[LockedWalletApi]
|
confirmations: Int): Future[LockedWalletApi]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the give block, updating our DB state if it's relevant to us.
|
||||||
|
* @param block The block we're processing
|
||||||
|
*/
|
||||||
|
def processBlock(block: Block, confirmations: Int): Future[LockedWalletApi]
|
||||||
|
|
||||||
/** Gets the sum of all UTXOs in this wallet */
|
/** Gets the sum of all UTXOs in this wallet */
|
||||||
def getBalance(): Future[CurrencyUnit] = {
|
def getBalance(): Future[CurrencyUnit] = {
|
||||||
val confirmedF = getConfirmedBalance()
|
val confirmedF = getConfirmedBalance()
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package org.bitcoins.wallet.internal
|
package org.bitcoins.wallet.internal
|
||||||
|
|
||||||
import org.bitcoins.core.protocol.transaction.Transaction
|
|
||||||
import org.bitcoins.wallet._
|
|
||||||
import org.bitcoins.wallet.models._
|
|
||||||
import scala.concurrent.Future
|
|
||||||
import org.bitcoins.core.protocol.transaction.TransactionOutput
|
|
||||||
import org.bitcoins.wallet.api.AddUtxoSuccess
|
|
||||||
import org.bitcoins.wallet.api.AddUtxoError
|
|
||||||
import org.bitcoins.core.number.UInt32
|
import org.bitcoins.core.number.UInt32
|
||||||
|
import org.bitcoins.core.protocol.blockchain.Block
|
||||||
|
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutput}
|
||||||
import org.bitcoins.core.util.FutureUtil
|
import org.bitcoins.core.util.FutureUtil
|
||||||
|
import org.bitcoins.wallet._
|
||||||
|
import org.bitcoins.wallet.api.{AddUtxoError, AddUtxoSuccess}
|
||||||
|
import org.bitcoins.wallet.models._
|
||||||
|
|
||||||
|
import scala.concurrent.Future
|
||||||
|
|
||||||
/** Provides functionality for processing transactions. This
|
/** Provides functionality for processing transactions. This
|
||||||
* includes importing UTXOs spent to our wallet, updating
|
* includes importing UTXOs spent to our wallet, updating
|
||||||
|
@ -35,6 +35,31 @@ private[wallet] trait TransactionProcessing extends KeyHandlingLogger {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def processBlock(
|
||||||
|
block: Block,
|
||||||
|
confirmations: Int): Future[LockedWallet] = {
|
||||||
|
logger.info(
|
||||||
|
s"Processing block=${block.blockHeader.hash.flip} with confirmations=$confirmations")
|
||||||
|
val res = block.transactions.foldLeft(Future.successful(this)) {
|
||||||
|
(acc, transaction) =>
|
||||||
|
for {
|
||||||
|
_ <- acc
|
||||||
|
newWallet <- processTransaction(transaction, confirmations)
|
||||||
|
} yield {
|
||||||
|
newWallet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.foreach(
|
||||||
|
_ =>
|
||||||
|
logger.info(
|
||||||
|
s"Finished processing of block=${block.blockHeader.hash.flip}."))
|
||||||
|
res.failed.foreach(e =>
|
||||||
|
logger.error(s"Error processing of block=${block.blockHeader.hash.flip}.",
|
||||||
|
e))
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
private[wallet] case class ProcessTxResult(
|
private[wallet] case class ProcessTxResult(
|
||||||
updatedIncoming: List[SpendingInfoDb],
|
updatedIncoming: List[SpendingInfoDb],
|
||||||
updatedOutgoing: List[SpendingInfoDb])
|
updatedOutgoing: List[SpendingInfoDb])
|
||||||
|
|
Loading…
Add table
Reference in a new issue