2024 09 30 dlcwallet has a wallet (#5692)

* refactor: Rework codebase so that DLCWallet has-a instance of Wallet rather than is-a via inheritance

* Fix cast

* Fix RescanHandling in DLCWallet

* Remove default implementation of WalletApi.broadcastTransaction()

* fix broadcast callback for DLCWallet

* Add DLCWalletDAOs.fromDLCAppConfig()

* Fix scaladoc for AddressHandlingApi.getUnusedAddress
This commit is contained in:
Chris Stewart 2024-10-01 13:29:58 -05:00 committed by GitHub
parent 5afa058892
commit 52c0625ba9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 610 additions and 399 deletions

View File

@ -2,7 +2,10 @@ package org.bitcoins.server
import org.apache.pekko.http.scaladsl.model.ContentTypes
import org.apache.pekko.http.scaladsl.testkit.ScalatestRouteTest
import org.bitcoins.core.api.dlc.wallet.DLCNeutrinoHDWalletApi
import org.bitcoins.core.api.dlc.wallet.{
DLCNeutrinoHDWalletApi,
IncomingDLCOfferHandlingApi
}
import org.bitcoins.core.api.dlc.wallet.db.DLCContactDb
import org.bitcoins.core.currency.{Bitcoins, Satoshis}
import org.bitcoins.core.protocol.dlc.models.ContractInfo
@ -30,6 +33,9 @@ class DLCRoutesSpec
val mockNodeApi = DLCNode(mockWallet)(system, conf.dlcNodeConf)
val mockIncomingDLCOfferHandlingApi: IncomingDLCOfferHandlingApi =
mock[IncomingDLCOfferHandlingApi]
val dlcRoutes = DLCRoutes(mockNodeApi)
// https://test.oracle.suredbits.com/announcement/8863cd80e1d37f668e27b84cbfed48541d671b4fed1462b86d547e7f13b5a9e4
@ -161,7 +167,11 @@ class DLCRoutesSpec
}
"contact-add a peer" in {
(mockWallet
(() => mockWallet.incomingOfferHandling)
.expects()
.returning(mockIncomingDLCOfferHandlingApi)
.anyNumberOfTimes()
(mockWallet.incomingOfferHandling
.addDLCContact(_: DLCContactDb))
.expects(expected)
.returning(Future.unit)
@ -178,7 +188,12 @@ class DLCRoutesSpec
}
"contacts-list list contacts" in {
(() => mockWallet.listDLCContacts())
(() => mockWallet.incomingOfferHandling)
.expects()
.returning(mockIncomingDLCOfferHandlingApi)
.anyNumberOfTimes()
(() => mockWallet.incomingOfferHandling.listDLCContacts())
.expects()
.returning(Future.successful(Vector(expected)))
@ -200,7 +215,12 @@ class DLCRoutesSpec
val address = host + port
val unresolved = InetSocketAddress.createUnresolved(host, 2862)
(mockWallet
(() => mockWallet.incomingOfferHandling)
.expects()
.returning(mockIncomingDLCOfferHandlingApi)
.anyNumberOfTimes()
(mockWallet.incomingOfferHandling
.removeDLCContact(_: InetSocketAddress))
.expects(unresolved)
.returning(Future.unit)
@ -216,7 +236,12 @@ class DLCRoutesSpec
}
"dlc-contact-add a peer" in {
(mockWallet
(() => mockWallet.incomingOfferHandling)
.expects()
.returning(mockIncomingDLCOfferHandlingApi)
.anyNumberOfTimes()
(mockWallet.incomingOfferHandling
.addDLCContactMapping(_: Sha256Digest, _: InetSocketAddress))
.expects(Sha256Digest.empty, expected.address)
.returning(Future.unit)
@ -236,8 +261,12 @@ class DLCRoutesSpec
}
"dlc-contact-remove a peer" in {
(() => mockWallet.incomingOfferHandling)
.expects()
.returning(mockIncomingDLCOfferHandlingApi)
.anyNumberOfTimes()
(mockWallet
(mockWallet.incomingOfferHandling
.removeDLCContactMapping(_: Sha256Digest))
.expects(Sha256Digest.empty)
.returning(Future.unit)

View File

@ -297,15 +297,18 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
// after we have created it into SyncUtil.getNodeApiWalletCallback
// so we don't lose the internal state of the wallet
val walletCallbackP = Promise[DLCWallet]()
val nodeApi = BitcoindRpcBackendUtil.buildBitcoindNodeApi(
bitcoind,
walletCallbackP.future,
chainCallbacksOpt
)
val pairedWallet = DLCWallet(
nodeApi = BitcoindRpcBackendUtil.buildBitcoindNodeApi(
bitcoind,
walletCallbackP.future,
chainCallbacksOpt
),
chainQueryApi = bitcoind
)(wallet.walletConfig, wallet.dlcConfig)
val walletConfig = wallet.walletConfig
val dlcConfig = wallet.dlcConfig
val bitcoindCallbackWallet =
wallet.walletApi.copy(nodeApi = nodeApi)(walletConfig)
val pairedWallet =
DLCWallet(bitcoindCallbackWallet)(dlcConfig, walletConfig)
walletCallbackP.success(pairedWallet)

View File

@ -76,7 +76,7 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem)
case ServerCommand("offers-list", _) =>
complete {
dlcNode.wallet.listIncomingDLCOffers().map { offers =>
dlcNode.incomingOfferHandling.listIncomingDLCOffers().map { offers =>
def toJson(io: IncomingDLCOfferDb): Value = {
Obj(
"hash" -> io.hash.hex,
@ -94,7 +94,7 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem)
case ServerCommand("offer-add", arr) =>
withValidServerCommand(OfferAdd.fromJsArr(arr)) { register =>
complete {
dlcNode.wallet
dlcNode.incomingOfferHandling
.registerIncomingDLCOffer(
register.offerTLV,
register.peer,
@ -109,9 +109,11 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem)
case ServerCommand("offer-remove", arr) =>
withValidServerCommand(OfferRemove.fromJsArr(arr)) { reject =>
complete {
dlcNode.wallet.rejectIncomingDLCOffer(reject.hash).map { _ =>
Server.httpSuccess(reject.hash.hex)
}
dlcNode.incomingOfferHandling
.rejectIncomingDLCOffer(reject.hash)
.map { _ =>
Server.httpSuccess(reject.hash.hex)
}
}
}
@ -138,7 +140,7 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem)
case ServerCommand("contacts-list", _) =>
complete {
dlcNode.wallet.listDLCContacts().map { contacts =>
dlcNode.incomingOfferHandling.listDLCContacts().map { contacts =>
val json = contacts
.map(c => upickle.default.writeJs(c)(Picklers.contactDbPickler))
Server.httpSuccess(json)
@ -148,7 +150,7 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem)
case ServerCommand("contact-add", arr) =>
withValidServerCommand(ContactAdd.fromJsArr(arr)) { contactAdd =>
complete {
dlcNode.wallet
dlcNode.incomingOfferHandling
.addDLCContact(contactAdd.toDLCContactDb)
.map { _ =>
Server.httpSuccess("ok")
@ -159,7 +161,7 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem)
case ServerCommand("contact-remove", arr) =>
withValidServerCommand(ContactRemove.fromJsArr(arr)) { contactAdd =>
complete {
dlcNode.wallet
dlcNode.incomingOfferHandling
.removeDLCContact(contactAdd.address)
.map { _ =>
Server.httpSuccess("ok")
@ -170,7 +172,7 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem)
case ServerCommand("dlc-contact-add", arr) =>
withValidServerCommand(DLCContactAdd.fromJsArr(arr)) { dlcContactAdd =>
complete {
dlcNode.wallet
dlcNode.incomingOfferHandling
.addDLCContactMapping(dlcContactAdd.dlcId, dlcContactAdd.address)
.map { _ =>
val dlcId = dlcContactAdd.dlcId.hex
@ -187,7 +189,7 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem)
withValidServerCommand(DLCContactRemove.fromJsArr(arr)) {
dlcContactRemove =>
complete {
dlcNode.wallet
dlcNode.incomingOfferHandling
.removeDLCContactMapping(dlcContactRemove.dlcId)
.map { _ =>
Server.httpSuccess("ok")

View File

@ -1,9 +1,12 @@
package org.bitcoins.core.api.dlc.node
import org.bitcoins.core.api.dlc.wallet.DLCWalletApi
import org.bitcoins.core.api.dlc.wallet.{
DLCWalletApi,
IncomingDLCOfferHandlingApi
}
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.dlc.models.DLCMessage
import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.protocol.tlv.*
import org.bitcoins.core.util.StartStopAsync
import org.bitcoins.crypto.Sha256Digest
@ -13,6 +16,8 @@ import scala.concurrent.Future
trait DLCNodeApi extends StartStopAsync[Unit] {
def wallet: DLCWalletApi
final def incomingOfferHandling: IncomingDLCOfferHandlingApi =
wallet.incomingOfferHandling
def acceptDLCOffer(
peerAddress: InetSocketAddress,

View File

@ -1,10 +1,6 @@
package org.bitcoins.core.api.dlc.wallet
import org.bitcoins.core.api.dlc.wallet.db.{
DLCContactDb,
DLCDb,
IncomingDLCOfferDb
}
import org.bitcoins.core.api.dlc.wallet.db.DLCDb
import org.bitcoins.core.api.wallet._
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.dlc.accounting._
@ -21,7 +17,9 @@ import scodec.bits.ByteVector
import java.net.InetSocketAddress
import scala.concurrent._
trait DLCWalletApi { self: WalletApi =>
trait DLCWalletApi {
protected def walletApi: WalletApi
def incomingOfferHandling: IncomingDLCOfferHandlingApi
def createDLCOffer(
contractInfoTLV: ContractInfoTLV,
@ -161,32 +159,6 @@ trait DLCWalletApi { self: WalletApi =>
/** Retrieves accounting and financial metrics for the entire dlc wallet */
def getWalletAccounting(): Future[DLCWalletAccounting]
def registerIncomingDLCOffer(
offerTLV: DLCOfferTLV,
peer: Option[String],
message: Option[String]): Future[Sha256Digest]
def listIncomingDLCOffers(): Future[Vector[IncomingDLCOfferDb]]
def rejectIncomingDLCOffer(offerHash: Sha256Digest): Future[Unit]
def findIncomingDLCOffer(
offerHash: Sha256Digest): Future[Option[IncomingDLCOfferDb]]
def listDLCContacts(): Future[Vector[DLCContactDb]]
def addDLCContact(contact: DLCContactDb): Future[Unit]
def removeDLCContact(address: InetSocketAddress): Future[Unit]
def findDLCContacts(alias: String): Future[Vector[DLCContactDb]]
def addDLCContactMapping(
dlcId: Sha256Digest,
contactId: InetSocketAddress): Future[Unit]
def removeDLCContactMapping(dlcId: Sha256Digest): Future[Unit]
def listDLCsByContact(address: InetSocketAddress): Future[Vector[DLCStatus]]
}

View File

@ -0,0 +1,36 @@
package org.bitcoins.core.api.dlc.wallet
import org.bitcoins.core.api.dlc.wallet.db.{DLCContactDb, IncomingDLCOfferDb}
import org.bitcoins.core.protocol.tlv.DLCOfferTLV
import org.bitcoins.crypto.Sha256Digest
import java.net.InetSocketAddress
import scala.concurrent.Future
trait IncomingDLCOfferHandlingApi {
def registerIncomingDLCOffer(
offerTLV: DLCOfferTLV,
peer: Option[String],
message: Option[String]): Future[Sha256Digest]
def listIncomingDLCOffers(): Future[Vector[IncomingDLCOfferDb]]
def rejectIncomingDLCOffer(offerHash: Sha256Digest): Future[Unit]
def findIncomingDLCOffer(
offerHash: Sha256Digest): Future[Option[IncomingDLCOfferDb]]
def listDLCContacts(): Future[Vector[DLCContactDb]]
def addDLCContact(contact: DLCContactDb): Future[Unit]
def removeDLCContact(address: InetSocketAddress): Future[Unit]
def findDLCContacts(alias: String): Future[Vector[DLCContactDb]]
def addDLCContactMapping(
dlcId: Sha256Digest,
contactId: InetSocketAddress): Future[Unit]
def removeDLCContactMapping(dlcId: Sha256Digest): Future[Unit]
}

View File

@ -2,7 +2,7 @@ package org.bitcoins.core.api.wallet
import org.bitcoins.core.api.wallet.db.{AddressDb, AddressTagDb, ScriptPubKeyDb}
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.hd.AddressType
import org.bitcoins.core.hd.{AddressType, HDAccount}
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.transaction.{
@ -16,9 +16,25 @@ import org.bitcoins.core.wallet.utxo.{
AddressTagType
}
import scala.concurrent.Future
import scala.concurrent.{ExecutionContext, Future}
trait AddressHandlingApi {
final def contains(
address: BitcoinAddress,
accountOpt: Option[(AccountHandlingApi, HDAccount)]
)(implicit ec: ExecutionContext): Future[Boolean] = {
val possibleAddressesF = accountOpt match {
case Some((ah, account)) =>
ah.listAddresses(account)
case None =>
listAddresses()
}
possibleAddressesF.map { possibleAddresses =>
possibleAddresses.exists(_.address == address)
}
}
def dropAddressTag(addressTagDb: AddressTagDb): Future[Int]
def dropAddressTagType(
addressTagType: AddressTagType
@ -55,11 +71,17 @@ trait AddressHandlingApi {
): Future[Option[AddressInfo]]
def getAddressTags(): Future[Vector[AddressTagDb]]
def getAddressTags(address: BitcoinAddress): Future[Vector[AddressTagDb]]
def getAddressTags(tagType: AddressTagType): Future[Vector[AddressTagDb]]
def getAddressTags(
address: BitcoinAddress,
tagType: AddressTagType
): Future[Vector[AddressTagDb]]
/** Gets a external address. Calling this method multiple times will return
* the same address, until it has received funds.
*/
def getUnusedAddress: Future[BitcoinAddress]
/** Determines if the given output is from this wallet and is a change output
* from this wallet
*/

View File

@ -35,8 +35,7 @@ trait WalletApi {
def feeRateApi: FeeRateApi
val creationTime: Instant
def broadcastTransaction(transaction: Transaction): Future[Unit] =
nodeApi.broadcastTransaction(transaction)
def broadcastTransaction(transaction: Transaction): Future[Unit]
def getFeeRate(): Future[FeeUnit] = feeRateApi.getFeeRate()

View File

@ -61,6 +61,7 @@ class DLCNegotiationTest extends BitcoinSDualWalletTest {
_ <- DLCClient.connect(
Peer(connectAddress, socks5ProxyParams = None),
walletB,
walletB.incomingOfferHandling,
Some(handlerP),
handleWrite = handleWriteFn,
handleWriteError = handleWriteErrorFn
@ -130,6 +131,7 @@ class DLCNegotiationTest extends BitcoinSDualWalletTest {
_ <- DLCClient.connect(
Peer(connectAddress, socks5ProxyParams = None),
walletB,
walletB.incomingOfferHandling,
Some(handlerP),
handleWrite = { (_, tlvId) =>
okP.success(tlvId)
@ -143,8 +145,8 @@ class DLCNegotiationTest extends BitcoinSDualWalletTest {
handler <- handlerP.future
preA <- walletA.listIncomingDLCOffers()
preB <- walletA.listIncomingDLCOffers()
preA <- walletA.incomingOfferHandling.listIncomingDLCOffers()
preB <- walletA.incomingOfferHandling.listIncomingDLCOffers()
_ = assert(preA.isEmpty)
_ = assert(preB.isEmpty)
@ -168,10 +170,10 @@ class DLCNegotiationTest extends BitcoinSDualWalletTest {
_ = assert(!errorP.isCompleted)
_ <- TestAsyncUtil.awaitConditionF { () =>
walletA.listIncomingDLCOffers().map(_.nonEmpty)
walletA.incomingOfferHandling.listIncomingDLCOffers().map(_.nonEmpty)
}
postA <- walletA.listIncomingDLCOffers()
postB <- walletB.listIncomingDLCOffers()
postA <- walletA.incomingOfferHandling.listIncomingDLCOffers()
postB <- walletB.incomingOfferHandling.listIncomingDLCOffers()
} yield {
assert(postA.nonEmpty)
assert(postB.isEmpty)

View File

@ -52,7 +52,7 @@ class DLCServerTest extends BitcoinSActorFixtureWithDLCWallet {
dlcWalletApi.wallet,
bindAddress,
Some(boundAddressPromise),
{ (_, _, connectionHandler) =>
{ (_, _, _, connectionHandler) =>
serverConnectionHandlerOpt = Some(connectionHandler)
serverProbe.ref
},
@ -70,9 +70,10 @@ class DLCServerTest extends BitcoinSActorFixtureWithDLCWallet {
val client = TestActorRef(
DLCClient.props(
dlcWalletApi.wallet,
dlcWalletApi.wallet.incomingOfferHandling,
Some(connectedAddressPromise),
None,
{ (_, _, connectionHandler) =>
{ (_, _, _, connectionHandler) =>
clientConnectionHandlerOpt = Some(connectionHandler)
clientProbe.ref
},

View File

@ -75,7 +75,7 @@ class DLCServerTorTest
fundedDLCWallet.wallet,
bindAddress,
Some(boundAddressPromise),
{ (_, _, connectionHandler) =>
{ (_, _, _, connectionHandler) =>
serverConnectionHandlerOpt = Some(connectionHandler)
serverProbe.ref
},
@ -94,9 +94,10 @@ class DLCServerTorTest
val client = TestActorRef(
DLCClient.props(
fundedDLCWallet.wallet,
fundedDLCWallet.wallet.incomingOfferHandling,
Some(connectedAddressPromise),
None,
{ (_, _, connectionHandler) =>
{ (_, _, _, connectionHandler) =>
clientConnectionHandlerOpt = Some(connectionHandler)
clientProbe.ref
},

View File

@ -10,7 +10,10 @@ import org.apache.pekko.actor.{
}
import org.apache.pekko.event.LoggingReceive
import org.apache.pekko.io.{IO, Tcp}
import org.bitcoins.core.api.dlc.wallet.DLCWalletApi
import org.bitcoins.core.api.dlc.wallet.{
DLCWalletApi,
IncomingDLCOfferHandlingApi
}
import org.bitcoins.core.api.node.Peer
import org.bitcoins.core.api.tor.Socks5ProxyParams
import org.bitcoins.core.protocol.BigSizeUInt
@ -25,6 +28,7 @@ import scala.concurrent.{Future, Promise}
class DLCClient(
dlcWalletApi: DLCWalletApi,
incomingOfferHandling: IncomingDLCOfferHandlingApi,
connectedAddress: Option[Promise[InetSocketAddress]],
handlerP: Option[Promise[ActorRef]],
dataHandlerFactory: DLCDataHandler.Factory,
@ -93,6 +97,7 @@ class DLCClient(
Props(
new DLCConnectionHandler(
dlcWalletApi,
incomingOfferHandling,
connection,
handlerP,
dataHandlerFactory,
@ -127,6 +132,7 @@ class DLCClient(
Props(
new DLCConnectionHandler(
dlcWalletApi,
incomingOfferHandling,
proxy,
handlerP,
dataHandlerFactory,
@ -155,6 +161,7 @@ object DLCClient {
def props(
dlcWalletApi: DLCWalletApi,
incomingOfferHandling: IncomingDLCOfferHandlingApi,
connectedAddress: Option[Promise[InetSocketAddress]],
handlerP: Option[Promise[ActorRef]],
dataHandlerFactory: DLCDataHandler.Factory,
@ -164,6 +171,7 @@ object DLCClient {
Props(
new DLCClient(
dlcWalletApi,
incomingOfferHandling,
connectedAddress,
handlerP,
dataHandlerFactory,
@ -175,6 +183,7 @@ object DLCClient {
def connect(
peer: Peer,
dlcWalletApi: DLCWalletApi,
incomingOfferHandling: IncomingDLCOfferHandlingApi,
handlerP: Option[Promise[ActorRef]],
dataHandlerFactory: DLCDataHandler.Factory =
DLCDataHandler.defaultFactory,
@ -186,6 +195,7 @@ object DLCClient {
system.actorOf(
props(
dlcWalletApi,
incomingOfferHandling,
Some(promise),
handlerP,
dataHandlerFactory,

View File

@ -5,9 +5,12 @@ import org.apache.pekko.event.LoggingReceive
import org.apache.pekko.io.Tcp
import org.apache.pekko.util.ByteString
import org.bitcoins.commons.util.BitcoinSLogger
import org.bitcoins.core.api.dlc.wallet.DLCWalletApi
import org.bitcoins.core.api.dlc.wallet.{
DLCWalletApi,
IncomingDLCOfferHandlingApi
}
import org.bitcoins.core.protocol.BigSizeUInt
import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.protocol.tlv.*
import org.bitcoins.dlc.node.DLCConnectionHandler.parseIndividualMessages
import scodec.bits.ByteVector
@ -18,6 +21,7 @@ import scala.util.{Failure, Success, Try}
class DLCConnectionHandler(
dlcWalletApi: DLCWalletApi,
incomingOfferHandling: IncomingDLCOfferHandlingApi,
connection: ActorRef,
handlerP: Option[Promise[ActorRef]],
dataHandlerFactory: DLCDataHandler.Factory,
@ -27,7 +31,8 @@ class DLCConnectionHandler(
with ActorLogging {
private val handler = {
val h = dataHandlerFactory(dlcWalletApi, context, self)
val h =
dataHandlerFactory(dlcWalletApi, incomingOfferHandling, context, self)
handlerP.foreach(_.success(h))
h
}
@ -142,6 +147,7 @@ object DLCConnectionHandler extends BitcoinSLogger {
def props(
dlcWalletApi: DLCWalletApi,
incomingOfferHandling: IncomingDLCOfferHandlingApi,
connection: ActorRef,
handlerP: Option[Promise[ActorRef]],
dataHandlerFactory: DLCDataHandler.Factory,
@ -151,6 +157,7 @@ object DLCConnectionHandler extends BitcoinSLogger {
Props(
new DLCConnectionHandler(
dlcWalletApi,
incomingOfferHandling,
connection,
handlerP,
dataHandlerFactory,

View File

@ -9,12 +9,18 @@ import org.apache.pekko.actor.{
Terminated
}
import org.apache.pekko.event.LoggingReceive
import org.bitcoins.core.api.dlc.wallet.DLCWalletApi
import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.api.dlc.wallet.{
DLCWalletApi,
IncomingDLCOfferHandlingApi
}
import org.bitcoins.core.protocol.tlv.*
import scala.concurrent._
import scala.concurrent.*
class DLCDataHandler(dlcWalletApi: DLCWalletApi, connectionHandler: ActorRef)
class DLCDataHandler(
dlcWalletApi: DLCWalletApi,
incomingOfferHandling: IncomingDLCOfferHandlingApi,
connectionHandler: ActorRef)
extends Actor
with ActorLogging {
implicit val ec: ExecutionContextExecutor = context.system.dispatcher
@ -58,11 +64,13 @@ class DLCDataHandler(dlcWalletApi: DLCWalletApi, connectionHandler: ActorRef)
Future.unit
case dlcOffer: DLCOfferTLV =>
for {
_ <- dlcWalletApi.registerIncomingDLCOffer(dlcOffer, None, None)
_ <- incomingOfferHandling.registerIncomingDLCOffer(dlcOffer,
None,
None)
} yield ()
case dlcOfferMessage: SendOfferTLV =>
for {
_ <- dlcWalletApi.registerIncomingDLCOffer(
_ <- incomingOfferHandling.registerIncomingDLCOffer(
offerTLV = dlcOfferMessage.offer,
peer = Some(dlcOfferMessage.peer),
message = Some(dlcOfferMessage.message)
@ -91,7 +99,11 @@ class DLCDataHandler(dlcWalletApi: DLCWalletApi, connectionHandler: ActorRef)
object DLCDataHandler {
type Factory = (DLCWalletApi, ActorContext, ActorRef) => ActorRef
type Factory = (
DLCWalletApi,
IncomingDLCOfferHandlingApi,
ActorContext,
ActorRef) => ActorRef
sealed trait Command
case class Received(message: LnMessage[TLV]) extends Command
@ -99,12 +111,20 @@ object DLCDataHandler {
def defaultFactory(
dlcWalletApi: DLCWalletApi,
incomingOfferHandling: IncomingDLCOfferHandlingApi,
context: ActorContext,
connectionHandler: ActorRef
): ActorRef = {
context.actorOf(props(dlcWalletApi, connectionHandler))
context.actorOf(
props(dlcWalletApi, incomingOfferHandling, connectionHandler))
}
def props(dlcWalletApi: DLCWalletApi, connectionHandler: ActorRef): Props =
Props(new DLCDataHandler(dlcWalletApi, connectionHandler))
def props(
dlcWalletApi: DLCWalletApi,
incomingOfferHandling: IncomingDLCOfferHandlingApi,
connectionHandler: ActorRef): Props =
Props(
new DLCDataHandler(dlcWalletApi,
incomingOfferHandling,
connectionHandler))
}

View File

@ -220,6 +220,7 @@ case class DLCNode(wallet: DLCWalletApi)(implicit
_ <- DLCClient.connect(
peer,
wallet,
wallet.incomingOfferHandling,
Some(handlerP),
handleWrite = handleTLVSendSucceed,
handleWriteError = handleTLVSendFailed

View File

@ -57,6 +57,7 @@ class DLCServer(
Props(
new DLCConnectionHandler(
dlcWalletApi,
dlcWalletApi.incomingOfferHandling,
connection,
None,
dataHandlerFactory,

View File

@ -544,7 +544,8 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
it must "cancel an offered DLC" in {
(FundedDLCWallets: (FundedDLCWallet, FundedDLCWallet)) =>
val walletA = FundedDLCWallets._1.wallet
val dlcWalletA = FundedDLCWallets._1.wallet
val walletApiA = dlcWalletA.walletApi
val offerData: DLCOffer = DLCWalletUtil.sampleDLCOffer
@ -555,11 +556,11 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
val announcementTLV = announcementTLVs.head
for {
oldBalance <- walletA.getBalance()
oldReserved <- walletA.spendingInfoDAO.findByTxoState(TxoState.Reserved)
oldBalance <- dlcWalletA.getBalance()
oldReserved <- walletApiA.utxoHandling.listUtxos(TxoState.Reserved)
_ = assert(oldReserved.isEmpty)
offer <- walletA.createDLCOffer(
offer <- dlcWalletA.createDLCOffer(
offerData.contractInfo,
offerData.collateral,
Some(offerData.feeRate),
@ -572,18 +573,18 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint))
_ <- walletA.cancelDLC(dlcId)
_ <- dlcWalletA.cancelDLC(dlcId)
announcementData <- walletA.announcementDAO.findByPublicKey(
announcementData <- dlcWalletA.announcementDAO.findByPublicKey(
announcementTLV.publicKey
)
nonceDbs <- walletA.oracleNonceDAO.findByAnnouncementIds(
nonceDbs <- dlcWalletA.oracleNonceDAO.findByAnnouncementIds(
announcementData.map(_.id.get)
)
balance <- walletA.getBalance()
reserved <- walletA.spendingInfoDAO.findByTxoState(TxoState.Reserved)
dlcOpt <- walletA.findDLC(dlcId)
balance <- dlcWalletA.getBalance()
reserved <- walletApiA.utxoHandling.listUtxos(TxoState.Reserved)
dlcOpt <- dlcWalletA.findDLC(dlcId)
} yield {
assert(balance == oldBalance)
assert(reserved.isEmpty)
@ -597,17 +598,19 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
it must "cancel an accepted DLC" in {
(FundedDLCWallets: (FundedDLCWallet, FundedDLCWallet)) =>
val walletA = FundedDLCWallets._1.wallet
val walletB = FundedDLCWallets._2.wallet
val dlcWalletA = FundedDLCWallets._1.wallet
val dlcWalletB = FundedDLCWallets._2.wallet
val walletApiB = dlcWalletB.walletApi
val offerData: DLCOffer = DLCWalletUtil.sampleDLCOffer
for {
oldBalance <- walletB.getBalance()
oldReserved <- walletB.spendingInfoDAO.findByTxoState(TxoState.Reserved)
oldBalance <- dlcWalletB.getBalance()
oldReserved <- walletApiB.utxoHandling.listUtxos(TxoState.Reserved)
_ = assert(oldReserved.isEmpty)
offer <- walletA.createDLCOffer(
offer <- dlcWalletA.createDLCOffer(
offerData.contractInfo,
offerData.collateral,
Some(offerData.feeRate),
@ -617,15 +620,15 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
None,
None
)
_ <- walletB.acceptDLCOffer(offer, None, None, None)
_ <- dlcWalletB.acceptDLCOffer(offer, None, None, None)
dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint))
_ <- walletB.cancelDLC(dlcId)
_ <- dlcWalletB.cancelDLC(dlcId)
balance <- walletB.getBalance()
reserved <- walletB.spendingInfoDAO.findByTxoState(TxoState.Reserved)
dlcOpt <- walletB.findDLC(dlcId)
balance <- dlcWalletB.getBalance()
reserved <- walletApiB.utxoHandling.listUtxos(TxoState.Reserved)
dlcOpt <- dlcWalletB.findDLC(dlcId)
} yield {
assert(balance == oldBalance)
assert(reserved.isEmpty)
@ -635,25 +638,28 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
it must "cancel a signed DLC" in {
(FundedDLCWallets: (FundedDLCWallet, FundedDLCWallet)) =>
val walletA = FundedDLCWallets._1.wallet
val walletB = FundedDLCWallets._2.wallet
val dlcWalletA = FundedDLCWallets._1.wallet
val dlcWalletB = FundedDLCWallets._2.wallet
val walletApiA = dlcWalletA.walletApi
val walletApiB = dlcWalletB.walletApi
val offerData: DLCOffer = DLCWalletUtil.sampleDLCOffer
for {
oldBalanceA <- walletA.getBalance()
oldReservedA <- walletA.spendingInfoDAO.findByTxoState(
oldBalanceA <- dlcWalletA.getBalance()
oldReservedA <- walletApiA.utxoHandling.listUtxos(
TxoState.Reserved
)
_ = assert(oldReservedA.isEmpty)
oldBalanceB <- walletB.getBalance()
oldReservedB <- walletB.spendingInfoDAO.findByTxoState(
oldBalanceB <- dlcWalletB.getBalance()
oldReservedB <- walletApiB.utxoHandling.listUtxos(
TxoState.Reserved
)
_ = assert(oldReservedB.isEmpty)
offer <- walletA.createDLCOffer(
offer <- dlcWalletA.createDLCOffer(
offerData.contractInfo,
offerData.collateral,
Some(offerData.feeRate),
@ -663,22 +669,22 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
None,
None
)
accept <- walletB.acceptDLCOffer(offer, None, None, None)
sign <- walletA.signDLC(accept)
_ <- walletB.addDLCSigs(sign)
accept <- dlcWalletB.acceptDLCOffer(offer, None, None, None)
sign <- dlcWalletA.signDLC(accept)
_ <- dlcWalletB.addDLCSigs(sign)
dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint))
_ <- walletA.cancelDLC(dlcId)
_ <- walletB.cancelDLC(dlcId)
_ <- dlcWalletA.cancelDLC(dlcId)
_ <- dlcWalletB.cancelDLC(dlcId)
balanceA <- walletA.getBalance()
reservedA <- walletA.spendingInfoDAO.findByTxoState(TxoState.Reserved)
dlcAOpt <- walletA.findDLC(dlcId)
balanceA <- dlcWalletA.getBalance()
reservedA <- walletApiA.utxoHandling.listUtxos(TxoState.Reserved)
dlcAOpt <- dlcWalletA.findDLC(dlcId)
balanceB <- walletB.getBalance()
reservedB <- walletB.spendingInfoDAO.findByTxoState(TxoState.Reserved)
dlcBOpt <- walletB.findDLC(dlcId)
balanceB <- dlcWalletB.getBalance()
reservedB <- walletApiB.utxoHandling.listUtxos(TxoState.Reserved)
dlcBOpt <- dlcWalletB.findDLC(dlcId)
} yield {
assert(balanceA == oldBalanceA)
assert(reservedA.isEmpty)

View File

@ -334,13 +334,12 @@ object DLCAppConfig
walletConf.hasWallet().flatMap { walletExists =>
if (walletExists) {
logger.info(s"Using pre-existing wallet")
val wallet =
DLCWallet(nodeApi, chainQueryApi)
Future.successful(wallet)
val walletF = walletConf.createHDWallet(nodeApi, chainQueryApi)
walletF.map(DLCWallet.apply)
} else {
logger.info(s"Creating new wallet")
val unInitializedWallet =
DLCWallet(nodeApi, chainQueryApi)
Wallet(nodeApi, chainQueryApi)
Wallet
.initialize(
@ -348,7 +347,7 @@ object DLCAppConfig
accountHandling = unInitializedWallet.accountHandling,
bip39PasswordOpt = bip39PasswordOpt
)
.map(_.asInstanceOf[DLCWallet])
.map(DLCWallet.apply)
}
}
}

View File

@ -1,12 +1,31 @@
package org.bitcoins.dlc.wallet
import org.bitcoins.commons.util.BitcoinSLogger
import org.bitcoins.core.api.chain.ChainQueryApi
import org.bitcoins.core.api.dlc.wallet.DLCNeutrinoHDWalletApi
import org.bitcoins.core.api.dlc.wallet.{
DLCNeutrinoHDWalletApi,
IncomingDLCOfferHandlingApi
}
import org.bitcoins.core.api.dlc.wallet.db.DLCDb
import org.bitcoins.core.api.feeprovider.FeeRateApi
import org.bitcoins.core.api.node.NodeApi
import org.bitcoins.core.api.wallet.{
AccountHandlingApi,
AddressHandlingApi,
BlockSyncState,
FundTransactionHandlingApi,
NeutrinoHDWalletApi,
RescanHandlingApi,
SendFundsHandlingApi,
SyncHeightDescriptor,
UtxoHandlingApi,
WalletInfo
}
import org.bitcoins.core.api.wallet.db.*
import org.bitcoins.core.config.NetworkParameters
import org.bitcoins.core.currency.*
import org.bitcoins.core.dlc.accounting.DLCWalletAccounting
import org.bitcoins.core.gcs.GolombFilter
import org.bitcoins.core.hd.*
import org.bitcoins.core.number.*
import org.bitcoins.core.protocol.*
@ -39,72 +58,72 @@ import org.bitcoins.dlc.wallet.util.{
IntermediaryDLCStatus
}
import org.bitcoins.wallet.config.WalletAppConfig
import org.bitcoins.wallet.internal.TransactionProcessing
import org.bitcoins.wallet.models.WalletDAOs
import org.bitcoins.wallet.internal.{
RescanHandling,
TransactionProcessing,
UtxoHandling
}
import org.bitcoins.wallet.models.{
AddressDAO,
ScriptPubKeyDAO,
TransactionDAO,
WalletDAOs
}
import org.bitcoins.wallet.{Wallet, WalletLogger}
import scodec.bits.ByteVector
import slick.dbio.*
import java.net.InetSocketAddress
import java.time.Instant
import scala.concurrent.Future
/** A [[Wallet]] with full DLC Functionality */
abstract class DLCWallet
extends Wallet
with DLCNeutrinoHDWalletApi
with IncomingDLCOffersHandling {
implicit val dlcConfig: DLCAppConfig
case class DLCWallet(override val walletApi: Wallet)(implicit
val dlcConfig: DLCAppConfig,
val walletConfig: WalletAppConfig)
extends DLCNeutrinoHDWalletApi
with BitcoinSLogger {
import dlcConfig.ec
import dlcConfig.profile.api._
private val networkParameters: NetworkParameters = walletConfig.network
private[wallet] val dlcWalletDAOs = DLCWalletDAOs.fromDLCAppConfig(dlcConfig)
private[bitcoins] val announcementDAO: OracleAnnouncementDataDAO =
OracleAnnouncementDataDAO()
private[bitcoins] val oracleNonceDAO: OracleNonceDAO = OracleNonceDAO()
dlcWalletDAOs.oracleAnnouncementDAO
private[bitcoins] val oracleNonceDAO: OracleNonceDAO =
dlcWalletDAOs.oracleNonceDAO
private[bitcoins] val dlcAnnouncementDAO: DLCAnnouncementDAO =
DLCAnnouncementDAO()
private[bitcoins] val dlcOfferDAO: DLCOfferDAO = DLCOfferDAO()
private[bitcoins] val dlcAcceptDAO: DLCAcceptDAO = DLCAcceptDAO()
private[bitcoins] val dlcDAO: DLCDAO = DLCDAO()
dlcWalletDAOs.dlcAnnouncementDAO
private[bitcoins] val dlcOfferDAO: DLCOfferDAO = dlcWalletDAOs.dlcOfferDAO
private[bitcoins] val dlcAcceptDAO: DLCAcceptDAO = dlcWalletDAOs.dlcAcceptDAO
private[bitcoins] val dlcDAO: DLCDAO = dlcWalletDAOs.dlcDAO
private[bitcoins] val contractDataDAO: DLCContractDataDAO =
DLCContractDataDAO()
private[bitcoins] val dlcInputsDAO: DLCFundingInputDAO = DLCFundingInputDAO()
private[bitcoins] val dlcSigsDAO: DLCCETSignaturesDAO = DLCCETSignaturesDAO()
private[bitcoins] val dlcRefundSigDAO: DLCRefundSigsDAO = DLCRefundSigsDAO()
private[bitcoins] val remoteTxDAO: DLCRemoteTxDAO = DLCRemoteTxDAO()
private[bitcoins] val incomingOfferDAO: IncomingDLCOfferDAO =
IncomingDLCOfferDAO()
dlcWalletDAOs.contractDataDAO
private[bitcoins] val dlcInputsDAO: DLCFundingInputDAO =
dlcWalletDAOs.dlcInputsDAO
private[bitcoins] val dlcSigsDAO: DLCCETSignaturesDAO =
dlcWalletDAOs.dlcSigsDAO
private[bitcoins] val dlcRefundSigDAO: DLCRefundSigsDAO =
dlcWalletDAOs.dlcRefundSigDAO
private[bitcoins] val remoteTxDAO: DLCRemoteTxDAO =
dlcWalletDAOs.dlcRemoteTxDAO
private[bitcoins] val contactDAO: DLCContactDAO =
DLCContactDAO()
dlcWalletDAOs.contactDAO
private val walletDAOs: WalletDAOs = WalletDAOs(accountDAO,
addressDAO,
addressTagDAO,
spendingInfoDAO,
transactionDAO,
incomingTxDAO,
outgoingTxDAO,
scriptPubKeyDAO,
stateDescriptorDAO)
private[wallet] val dlcWalletDAOs = DLCWalletDAOs(
dlcDAO,
contractDataDAO,
dlcAnnouncementDAO,
dlcInputsDAO,
dlcOfferDAO,
dlcAcceptDAO,
dlcSigsDAO,
dlcRefundSigDAO,
oracleNonceDAO,
announcementDAO,
remoteTxDAO,
incomingOfferDAO,
contactDAO
)
private def walletDAOs: WalletDAOs = walletApi.walletDAOs
private[bitcoins] def addressDAO: AddressDAO = walletApi.addressDAO
private[bitcoins] def transactionDAO: TransactionDAO =
walletApi.transactionDAO
private[bitcoins] def scriptPubKeyDAO: ScriptPubKeyDAO =
walletApi.scriptPubKeyDAO
override def incomingOfferHandling: IncomingDLCOfferHandlingApi =
IncomingDLCOffersHandling(dlcWalletDAOs)
private[wallet] val dlcDataManagement = DLCDataManagement(dlcWalletDAOs)
@ -116,22 +135,34 @@ abstract class DLCWallet
val txProcessing = TransactionProcessing(
walletApi = this,
chainQueryApi = chainQueryApi,
utxoHandling = utxoHandling,
utxoHandling = utxoHandling.asInstanceOf[UtxoHandling],
walletDAOs = walletDAOs
)
)(walletApi.walletConfig, ec)
DLCTransactionProcessing(
txProcessing = txProcessing,
dlcWalletDAOs = dlcWalletDAOs,
walletDAOs = walletDAOs,
dlcDataManagement = dlcDataManagement,
keyManager = keyManager,
transactionDAO = transactionDAO,
keyManager = walletApi.keyManager,
transactionDAO = walletDAOs.transactionDAO,
utxoHandling = utxoHandling,
dlcWalletApi = this
)
}
override lazy val rescanHandling: RescanHandlingApi = {
RescanHandling(
transactionProcessing = transactionProcessing,
accountHandling = accountHandling,
addressHandling = addressHandling,
chainQueryApi = chainQueryApi,
nodeApi = nodeApi,
walletDAOs = walletDAOs
)(walletConfig, walletConfig.system)
}
private lazy val safeDLCDatabase: SafeDatabase = dlcDAO.safeDatabase
private lazy val walletDatabase: SafeDatabase = addressDAO.safeDatabase
private lazy val walletDatabase: SafeDatabase =
walletDAOs.addressDAO.safeDatabase
/** Updates the contract Id in the wallet database for the given offer and
* accept
@ -299,8 +330,8 @@ abstract class DLCWallet
index: Int
): Future[Vector[AddressDb]] = {
for {
zero <- addressHandling.getAddress(account, chainType, index)
one <- addressHandling.getAddress(account, chainType, index + 1)
zero <- walletApi.addressHandling.getAddress(account, chainType, index)
one <- walletApi.addressHandling.getAddress(account, chainType, index + 1)
} yield {
logger.debug(s"Wrote DLC key addresses to database using index $index")
Vector(zero, one)
@ -331,7 +362,7 @@ abstract class DLCWallet
s"Canceling DLC with tempContractId=${dlcDb.tempContractId.hex} dlcId=${dlcId.hex} contractId=${dlcDb.contractIdOpt}"
)
inputs <- dlcInputsDAO.findByDLCId(dlcId, dlcDb.isInitiator)
dbs <- spendingInfoDAO.findByOutPoints(inputs.map(_.outPoint))
dbs <- walletDAOs.utxoDAO.findByOutPoints(inputs.map(_.outPoint))
// allow this to fail in the case they have already been unreserved
_ <- utxoHandling.unmarkUTXOsAsReserved(dbs).recoverWith {
case scala.util.control.NonFatal(_) => Future.successful(Vector.empty)
@ -430,10 +461,11 @@ abstract class DLCWallet
chainType = HDChainType.External
account <- accountHandling.getDefaultAccountForType(AddressType.SegWit)
nextIndex <- addressHandling.getNextAvailableIndex(account, chainType)
nextIndex <- walletApi.addressHandling.getNextAvailableIndex(account,
chainType)
_ <- writeDLCKeysToAddressDb(account, chainType, nextIndex)
fundRawTxHelper <- fundTxHandling.fundRawTransactionInternal(
fundRawTxHelper <- walletApi.fundTxHandling.fundRawTransactionInternal(
destinations = Vector(TransactionOutput(collateral, EmptyScriptPubKey)),
feeRate = feeRate,
fromAccount = account,
@ -635,7 +667,7 @@ abstract class DLCWallet
}
case None =>
val nextIndexF =
addressHandling.getNextAvailableIndex(account, chainType)
walletApi.addressHandling.getNextAvailableIndex(account, chainType)
val acceptWithoutSigsWithKeysF
: Future[(DLCAcceptWithoutSigs, DLCPublicKeys)] =
nextIndexF.map { nextIndex =>
@ -838,7 +870,7 @@ abstract class DLCWallet
val txBuilderAndSpendingInfosF
: Future[FundRawTxHelper[ShufflingNonInteractiveFinalizer]] = {
for {
fundRawTxHelper <- fundTxHandling.fundRawTransactionInternal(
fundRawTxHelper <- walletApi.fundTxHandling.fundRawTransactionInternal(
destinations =
Vector(TransactionOutput(collateral, EmptyScriptPubKey)),
feeRate = offer.feeRate,
@ -862,7 +894,7 @@ abstract class DLCWallet
)
)
val privKeyPath = HDPath.fromString(bip32Path.toString)
keyManager.toSign(privKeyPath)
walletApi.keyManager.toSign(privKeyPath)
}
private def createNewDLCAccept(
@ -1215,7 +1247,7 @@ abstract class DLCWallet
dlcId = dlc.dlcId,
transactionDAO = transactionDAO,
fundingUtxoScriptSigParams = scriptSigParams,
keyManager = keyManager
keyManager = walletApi.keyManager
)
mySigs <- dlcSigsDAO.findByDLCId(dlc.dlcId)
@ -1574,7 +1606,7 @@ abstract class DLCWallet
dlcDb = dlcDb,
fundingUtxoScriptSigParams = scriptSigParams,
transactionDAO = transactionDAO,
keyManager = keyManager
keyManager = walletApi.keyManager
)
_ = require(
signerOpt.isDefined,
@ -1635,11 +1667,13 @@ abstract class DLCWallet
isValidBroadcastState(dlcDb)
}
tx <- fundingTxF
_ <- updateDLCState(contractId, DLCState.Broadcasted)
state <- updateDLCState(contractId, DLCState.Broadcasted)
_ = logger.info(
s"Broadcasting funding transaction ${tx.txIdBE.hex} for contract ${contractId.toHex}"
)
_ <- broadcastTransaction(tx)
status <- findDLC(state.dlcId)
_ <- dlcConfig.walletCallbacks.executeOnDLCStateChange(status.get)
_ = logger.info(s"Done broadcast tx ${contractId}")
} yield tx
}
@ -1773,7 +1807,7 @@ abstract class DLCWallet
contractId = contractId,
txDAO = transactionDAO,
fundingUtxoScriptSigParams = scriptSigParams,
keyManager = keyManager
keyManager = walletApi.keyManager
)
}
executorWithSetupOptF.flatMap {
@ -1879,7 +1913,7 @@ abstract class DLCWallet
dlcDb.dlcId,
transactionDAO,
scriptSigParams,
keyManager
walletApi.keyManager
)
_ = require(
executorOpt.isDefined,
@ -2254,6 +2288,62 @@ abstract class DLCWallet
case Some(feeRate) =>
Future.successful(feeRate)
}
override def broadcastTransaction(transaction: Transaction): Future[Unit] = {
walletApi.broadcastTransaction(transaction)
}
override def processCompactFilters(
blockFilters: Vector[(DoubleSha256DigestBE, GolombFilter)])
: Future[NeutrinoHDWalletApi] =
walletApi.processCompactFilters(blockFilters)
override def accountHandling: AccountHandlingApi = walletApi.accountHandling
override def fundTxHandling: FundTransactionHandlingApi =
walletApi.fundTxHandling
override def addressHandling: AddressHandlingApi = walletApi.addressHandling
override def utxoHandling: UtxoHandlingApi = walletApi.utxoHandling
override def sendFundsHandling: SendFundsHandlingApi =
walletApi.sendFundsHandling
override val nodeApi: NodeApi = walletApi.nodeApi
override val chainQueryApi: ChainQueryApi = walletApi.chainQueryApi
override def feeRateApi: FeeRateApi = walletApi.feeRateApi
override val creationTime: Instant = walletApi.creationTime
/** Gets the sum of all confirmed UTXOs in this wallet */
override def getConfirmedBalance(): Future[CurrencyUnit] =
walletApi.getConfirmedBalance()
override def getNewAddress(): Future[BitcoinAddress] =
walletApi.getNewAddress()
override def getNewChangeAddress(): Future[BitcoinAddress] =
walletApi.getNewChangeAddress()
/** Gets the sum of all unconfirmed UTXOs in this wallet */
override def getUnconfirmedBalance(): Future[CurrencyUnit] =
walletApi.getUnconfirmedBalance()
/** Checks if the wallet contains any data */
override def isEmpty(): Future[Boolean] = walletApi.isEmpty()
override def getSyncState(): Future[BlockSyncState] = walletApi.getSyncState()
override def isRescanning(): Future[Boolean] = walletApi.isRescanning()
override def getSyncDescriptorOpt(): Future[Option[SyncHeightDescriptor]] =
walletApi.getSyncDescriptorOpt()
override def getWalletName(): Future[String] = walletApi.getWalletName()
override def getInfo(): Future[WalletInfo] = walletApi.getInfo()
}
object DLCWallet extends WalletLogger {
@ -2264,21 +2354,6 @@ object DLCWallet extends WalletLogger {
case class InvalidAnnouncementSignature(message: String)
extends RuntimeException(message)
private case class DLCWalletImpl(
nodeApi: NodeApi,
chainQueryApi: ChainQueryApi
)(implicit
val walletConfig: WalletAppConfig,
val dlcConfig: DLCAppConfig
) extends DLCWallet
def apply(
nodeApi: NodeApi,
chainQueryApi: ChainQueryApi
)(implicit config: WalletAppConfig, dlcConfig: DLCAppConfig): DLCWallet = {
DLCWalletImpl(nodeApi, chainQueryApi)
}
private object AcceptingOffersLatch {
private val tempContractIds =

View File

@ -1,5 +1,6 @@
package org.bitcoins.dlc.wallet.internal
import org.bitcoins.core.api.dlc.wallet.IncomingDLCOfferHandlingApi
import org.bitcoins.core.api.dlc.wallet.db.{
DLCContactDb,
DLCContactDbHelper,
@ -7,15 +8,25 @@ import org.bitcoins.core.api.dlc.wallet.db.{
}
import org.bitcoins.core.protocol.tlv.DLCOfferTLV
import org.bitcoins.crypto.Sha256Digest
import org.bitcoins.dlc.wallet.DLCWallet
import org.bitcoins.dlc.wallet.models.IncomingDLCOfferDbHelper
import org.bitcoins.dlc.wallet.DLCAppConfig
import org.bitcoins.dlc.wallet.models.{
DLCContactDAO,
DLCDAO,
DLCWalletDAOs,
IncomingDLCOfferDbHelper
}
import java.net.InetSocketAddress
import scala.concurrent.Future
trait IncomingDLCOffersHandling { self: DLCWallet =>
case class IncomingDLCOffersHandling(dlcWalletDAOs: DLCWalletDAOs)(implicit
dlcConfig: DLCAppConfig)
extends IncomingDLCOfferHandlingApi {
import dlcConfig.ec
def registerIncomingDLCOffer(
private val contactDAO: DLCContactDAO = dlcWalletDAOs.contactDAO
private val dlcDAO: DLCDAO = dlcWalletDAOs.dlcDAO
override def registerIncomingDLCOffer(
offerTLV: DLCOfferTLV,
peerOpt: Option[String],
message: Option[String]

View File

@ -1,5 +1,7 @@
package org.bitcoins.dlc.wallet.models
import org.bitcoins.dlc.wallet.DLCAppConfig
case class DLCWalletDAOs(
dlcDAO: DLCDAO,
contractDataDAO: DLCContractDataDAO,
@ -15,3 +17,37 @@ case class DLCWalletDAOs(
incomingDLCOfferDAO: IncomingDLCOfferDAO,
contactDAO: DLCContactDAO
)
object DLCWalletDAOs {
def fromDLCAppConfig(dlcConfig: DLCAppConfig): DLCWalletDAOs = {
val dlcDAO = DLCDAO()(dlcConfig.ec, dlcConfig)
val contractDAO = DLCContractDataDAO()(dlcConfig.ec, dlcConfig)
val dlcAnnouncementDAO = DLCAnnouncementDAO()(dlcConfig.ec, dlcConfig)
val dlcInputsDAO = DLCFundingInputDAO()(dlcConfig.ec, dlcConfig)
val dlcOfferDAO = DLCOfferDAO()(dlcConfig.ec, dlcConfig)
val dlcAcceptDAO = DLCAcceptDAO()(dlcConfig.ec, dlcConfig)
val dlcSigsDAO = DLCCETSignaturesDAO()(dlcConfig.ec, dlcConfig)
val dlcRefundSigDAO = DLCRefundSigsDAO()(dlcConfig.ec, dlcConfig)
val oracleNonceDAO = OracleNonceDAO()(dlcConfig.ec, dlcConfig)
val oracleAnnouncementDAO =
OracleAnnouncementDataDAO()(dlcConfig.ec, dlcConfig)
val dlcRemoteTxDAO = DLCRemoteTxDAO()(dlcConfig.ec, dlcConfig)
val incomingDLCOfferDAO = IncomingDLCOfferDAO()(dlcConfig.ec, dlcConfig)
val contactDAO = DLCContactDAO()(dlcConfig.ec, dlcConfig)
DLCWalletDAOs(
dlcDAO = dlcDAO,
contractDataDAO = contractDAO,
dlcAnnouncementDAO = dlcAnnouncementDAO,
dlcInputsDAO = dlcInputsDAO,
dlcOfferDAO = dlcOfferDAO,
dlcAcceptDAO = dlcAcceptDAO,
dlcSigsDAO = dlcSigsDAO,
dlcRefundSigDAO = dlcRefundSigDAO,
oracleNonceDAO = oracleNonceDAO,
oracleAnnouncementDAO = oracleAnnouncementDAO,
dlcRemoteTxDAO = dlcRemoteTxDAO,
incomingDLCOfferDAO = incomingDLCOfferDAO,
contactDAO = contactDAO
)
}
}

View File

@ -304,17 +304,17 @@ object BitcoinSWalletTest extends WalletLogger {
} yield ()
initConfs.flatMap { _ =>
val wallet =
DLCWallet(nodeApi, chainQueryApi)(
config.walletConf,
config.dlcConf
)
val wallet = Wallet(nodeApi, chainQueryApi)(config.walletConf)
Wallet
.initialize(wallet,
wallet.accountHandling,
config.walletConf.bip39PasswordOpt)
.map(_.asInstanceOf[DLCWallet])
.map(w =>
DLCWallet(w)(
config.dlcConf,
config.walletConf
))
}
}
@ -521,8 +521,8 @@ object BitcoinSWalletTest extends WalletLogger {
} yield ()
}
def destroyDLCWallet(wallet: DLCWallet): Future[Unit] = {
import wallet.ec
def destroyDLCWallet(wallet: DLCWallet)(implicit
ec: ExecutionContext): Future[Unit] = {
for {
_ <- destroyWalletAppConfig(wallet.walletConfig)
_ <- wallet.dlcConfig.stop()

View File

@ -403,9 +403,13 @@ object DLCWalletUtil extends BitcoinSLogger {
tx <- walletB.broadcastDLCFundingTx(sigs.contractId)
_ <- walletA.transactionProcessing.processTransaction(tx, None)
} yield {
val fundedA =
FundedDLCWallet(walletA, walletA.walletConfig, walletA.dlcConfig)
val fundedB =
FundedDLCWallet(walletB, walletB.walletConfig, walletB.dlcConfig)
(
InitializedDLCWallet(FundedDLCWallet(walletA)),
InitializedDLCWallet(FundedDLCWallet(walletB))
InitializedDLCWallet(fundedA),
InitializedDLCWallet(fundedB)
)
}
}

View File

@ -4,14 +4,14 @@ import org.apache.pekko.actor.ActorSystem
import org.bitcoins.commons.util.BitcoinSLogger
import org.bitcoins.core.api.chain.ChainQueryApi
import org.bitcoins.core.api.node.NodeApi
import org.bitcoins.core.api.wallet.NeutrinoHDWalletApi
import org.bitcoins.core.api.wallet.{NeutrinoHDWalletApi, WalletApi}
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.hd.HDAccount
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutput}
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.crypto.DoubleSha256DigestBE
import org.bitcoins.dlc.wallet.DLCWallet
import org.bitcoins.dlc.wallet.{DLCAppConfig, DLCWallet}
import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.server.{BitcoinSAppConfig, BitcoindRpcBackendUtil}
import org.bitcoins.testkit.wallet.BitcoinSWalletTest.{
@ -24,7 +24,6 @@ import org.bitcoins.testkit.wallet.FundWalletUtil.{
}
import org.bitcoins.testkitcore.gen.TransactionGenerators
import org.bitcoins.testkitcore.util.TransactionTestUtil
import org.bitcoins.wallet.Wallet
import org.bitcoins.wallet.config.WalletAppConfig
import scala.concurrent.{ExecutionContext, Future}
@ -64,8 +63,8 @@ trait FundWalletUtil extends BitcoinSLogger {
def fundAccountForWallet(
amts: Vector[CurrencyUnit],
account: HDAccount,
wallet: Wallet
)(implicit ec: ExecutionContext): Future[Wallet] = {
wallet: WalletApi
)(implicit ec: ExecutionContext): Future[WalletApi] = {
val addressesF: Future[Vector[BitcoinAddress]] = Future.sequence {
Vector.fill(3)(wallet.accountHandling.getNewAddress(account))
@ -132,17 +131,18 @@ trait FundWalletUtil extends BitcoinSLogger {
/** Funds a bitcoin-s wallet with 3 utxos with 1, 2 and 3 bitcoin in the utxos
*/
def fundWallet(
wallet: Wallet
wallet: WalletApi,
walletConfig: WalletAppConfig
)(implicit ec: ExecutionContext): Future[FundedTestWallet] = {
val defaultAccount = wallet.walletConfig.defaultAccount
val defaultAccount = walletConfig.defaultAccount
val fundedDefaultAccountWalletF = FundWalletUtil.fundAccountForWallet(
amts = BitcoinSWalletTest.defaultAcctAmts,
account = defaultAccount,
wallet = wallet
)
val hdAccount1 = WalletTestUtil.getHdAccount1(wallet.walletConfig)
val hdAccount1 = WalletTestUtil.getHdAccount1(walletConfig)
val fundedAccount1WalletF = for {
fundedDefaultAcct <- fundedDefaultAccountWalletF
fundedAcct1 <- FundWalletUtil.fundAccountForWallet(
@ -169,35 +169,28 @@ trait FundWalletUtil extends BitcoinSLogger {
s"got balance=${hdAccount1} expected=${BitcoinSWalletTest.expectedAccount1Amt}"
)
} yield FundedWallet(fundedWallet)
} yield FundedWallet(fundedWallet, walletConfig)
}
}
object FundWalletUtil extends FundWalletUtil {
trait FundedTestWallet {
def wallet: Wallet
}
object FundedTestWallet {
def apply(wallet: Wallet): FundedTestWallet = {
wallet match {
case dlc: DLCWallet =>
FundedDLCWallet(dlc)
case _: Wallet =>
FundedWallet(wallet)
}
}
def wallet: WalletApi
}
/** This is a wallet that was two funded accounts Account 0 (default account)
* has utxos of 1,2,3 bitcoin in it (6 btc total) Account 1 has a utxos of
* 0.2,0.3,0.5 bitcoin in it (0.6 total)
*/
case class FundedWallet(wallet: Wallet) extends FundedTestWallet
case class FundedWallet(wallet: WalletApi, walletConfig: WalletAppConfig)
extends FundedTestWallet
case class FundedDLCWallet(wallet: DLCWallet) extends FundedTestWallet
case class FundedDLCWallet(
wallet: DLCWallet,
walletConfig: WalletAppConfig,
dlcConfig: DLCAppConfig)
extends FundedTestWallet
/** This creates a wallet that was two funded accounts Account 0 (default
* account) has utxos of 1,2,3 bitcoin in it (6 btc total) Account 1 has a
@ -214,8 +207,8 @@ object FundWalletUtil extends FundWalletUtil {
nodeApi = nodeApi,
chainQueryApi = chainQueryApi
)
funded <- FundWalletUtil.fundWallet(wallet)
} yield FundedWallet(funded.wallet)
funded <- FundWalletUtil.fundWallet(wallet, config)
} yield FundedWallet(funded.wallet, config)
}
def createFundedDLCWallet(nodeApi: NodeApi, chainQueryApi: ChainQueryApi)(
@ -229,9 +222,11 @@ object FundWalletUtil extends FundWalletUtil {
nodeApi = nodeApi,
chainQueryApi = chainQueryApi
)
funded <- FundWalletUtil.fundWallet(wallet)
funded <- FundWalletUtil.fundWallet(wallet, config.walletConf)
} yield {
FundedDLCWallet(funded.wallet.asInstanceOf[DLCWallet])
FundedDLCWallet(funded.wallet.asInstanceOf[DLCWallet],
config.walletConf,
config.dlcConf)
}
}
@ -264,7 +259,9 @@ object FundWalletUtil extends FundWalletUtil {
bitcoind
)
} yield {
FundedDLCWallet(funded.asInstanceOf[DLCWallet])
FundedDLCWallet(funded.asInstanceOf[DLCWallet],
wallet.walletConfig,
wallet.dlcConfig)
}
}
}

View File

@ -11,6 +11,7 @@ import org.bitcoins.core.wallet.utxo.{
}
import org.bitcoins.testkit.wallet.FundWalletUtil.FundedWallet
import org.bitcoins.testkit.wallet.{BitcoinSWalletTest, WalletTestUtil}
import org.bitcoins.wallet.models.{ScriptPubKeyDAO, SpendingInfoDAO}
import org.scalatest.FutureOutcome
import scala.concurrent.Future
@ -40,7 +41,7 @@ class AddressHandlingTest extends BitcoinSWalletTest {
it must "generate an address for a non default account and then find it" in {
(fundedWallet: FundedWallet) =>
val wallet = fundedWallet.wallet
val account1 = WalletTestUtil.getHdAccount1(wallet.walletConfig)
val account1 = WalletTestUtil.getHdAccount1(fundedWallet.walletConfig)
val addressF = wallet.accountHandling.getNewAddress(account1)
for {
address <- addressF
@ -116,7 +117,7 @@ class AddressHandlingTest extends BitcoinSWalletTest {
tx <- wallet.sendFundsHandling.sendToAddress(tempAddress,
Bitcoins(1),
None)
spentDbs <- wallet.spendingInfoDAO.findOutputsBeingSpent(tx)
spentDbs <- wallet.utxoHandling.findOutputsBeingSpent(tx)
spentAddresses <- wallet.addressHandling.listSpentAddresses()
} yield {
val diff = spentDbs
@ -131,7 +132,7 @@ class AddressHandlingTest extends BitcoinSWalletTest {
val wallet = fundedWallet.wallet
for {
unspentDbs <- wallet.spendingInfoDAO.findAllUnspent()
unspentDbs <- wallet.utxoHandling.listUtxos()
fundedAddresses <- wallet.addressHandling.listFundedAddresses()
} yield {
val diff = unspentDbs
@ -147,9 +148,10 @@ class AddressHandlingTest extends BitcoinSWalletTest {
it must "get the correct unused addresses" in {
(fundedWallet: FundedWallet) =>
val wallet = fundedWallet.wallet
val spendingInfoDAO =
SpendingInfoDAO()(executionContext, fundedWallet.walletConfig)
for {
addrDbs <- wallet.spendingInfoDAO.findAllSpendingInfos()
addrDbs <- spendingInfoDAO.findAllSpendingInfos()
fundedAddresses <- wallet.addressHandling.listUnusedAddresses()
} yield {
val intersect = addrDbs
@ -269,11 +271,11 @@ class AddressHandlingTest extends BitcoinSWalletTest {
(fundedWallet: FundedWallet) =>
val wallet = fundedWallet.wallet
val addressF = wallet.getNewAddress()
val spendingInfoDAO = wallet.spendingInfoDAO
val spkDAO = wallet.scriptPubKeyDAO
val spkDAO =
ScriptPubKeyDAO()(executionContext, fundedWallet.walletConfig)
for {
address <- addressF
utxos <- spendingInfoDAO.findByScriptPubKey(address.scriptPubKey)
utxos <- wallet.utxoHandling.findByScriptPubKey(address.scriptPubKey)
spkOpt <- spkDAO.findScriptPubKey(address.scriptPubKey)
} yield {
assert(utxos.isEmpty)

View File

@ -43,7 +43,7 @@ class WalletCallbackTest extends BitcoinSWalletTest {
val callbacks = WalletCallbacks.onNewAddressGenerated(callback)
fundedWallet.wallet.walletConfig.addCallbacks(callbacks)
fundedWallet.walletConfig.addCallbacks(callbacks)
val wallet = fundedWallet.wallet
@ -68,7 +68,7 @@ class WalletCallbackTest extends BitcoinSWalletTest {
val callbacks = WalletCallbacks.onTransactionProcessed(callback)
fundedWallet.wallet.walletConfig.addCallbacks(callbacks)
fundedWallet.walletConfig.addCallbacks(callbacks)
val wallet = fundedWallet.wallet
@ -94,7 +94,7 @@ class WalletCallbackTest extends BitcoinSWalletTest {
val callbacks = WalletCallbacks.onTransactionProcessed(callback)
fundedWallet.wallet.walletConfig.addCallbacks(callbacks)
fundedWallet.walletConfig.addCallbacks(callbacks)
val wallet = fundedWallet.wallet
@ -132,7 +132,7 @@ class WalletCallbackTest extends BitcoinSWalletTest {
val callbacks = WalletCallbacks.onTransactionBroadcast(callback)
fundedWallet.wallet.walletConfig.addCallbacks(callbacks)
fundedWallet.walletConfig.addCallbacks(callbacks)
val wallet = fundedWallet.wallet
@ -155,7 +155,7 @@ class WalletCallbackTest extends BitcoinSWalletTest {
val callbacks = WalletCallbacks.onReservedUtxos(callback)
fundedWallet.wallet.walletConfig.addCallbacks(callbacks)
fundedWallet.walletConfig.addCallbacks(callbacks)
val wallet = fundedWallet.wallet
@ -187,7 +187,7 @@ class WalletCallbackTest extends BitcoinSWalletTest {
for {
utxos <- wallet.utxoHandling.listUtxos()
reserved <- wallet.utxoHandling.markUTXOsAsReserved(Vector(utxos.head))
_ = fundedWallet.wallet.walletConfig.addCallbacks(callbacks)
_ = fundedWallet.walletConfig.addCallbacks(callbacks)
_ <- wallet.utxoHandling.unmarkUTXOsAsReserved(reserved)
result <- resultP.future
@ -208,7 +208,7 @@ class WalletCallbackTest extends BitcoinSWalletTest {
val callbacks = WalletCallbacks.onBlockProcessed(callback)
fundedWallet.wallet.walletConfig.addCallbacks(callbacks)
fundedWallet.walletConfig.addCallbacks(callbacks)
val wallet = fundedWallet.wallet

View File

@ -17,6 +17,8 @@ import org.bitcoins.testkit.wallet.BitcoinSWalletTest
import org.bitcoins.testkit.wallet.FundWalletUtil.FundedWallet
import org.bitcoins.testkitcore.Implicits.GeneratorOps
import org.bitcoins.testkitcore.gen.FeeUnitGen
import org.bitcoins.wallet.config.WalletAppConfig
import org.bitcoins.wallet.models.{OutgoingTransactionDAO, SpendingInfoDAO}
import org.scalatest.{Assertion, FutureOutcome}
import scodec.bits.ByteVector
@ -113,9 +115,9 @@ class WalletSendingTest extends BitcoinSWalletTest {
}
def testOpReturnCommitment(
wallet: Wallet,
wallet: WalletApi,
hashMessage: Boolean
): Future[Assertion] = {
)(implicit walletConfig: WalletAppConfig): Future[Assertion] = {
val message = "ben was here"
for {
tx <- wallet.sendFundsHandling.makeOpReturnCommitment(
@ -123,8 +125,8 @@ class WalletSendingTest extends BitcoinSWalletTest {
hashMessage = hashMessage,
feeRateOpt = None
)
outgoingTxDbOpt <- wallet.outgoingTxDAO.read(tx.txIdBE)
outgoingTxDAO = OutgoingTransactionDAO()(executionContext, walletConfig)
outgoingTxDbOpt <- outgoingTxDAO.read(tx.txIdBE)
} yield {
val opReturnOutputOpt = tx.outputs.find(_.value == 0.satoshis)
assert(opReturnOutputOpt.isDefined, "Missing output with 0 value")
@ -157,12 +159,14 @@ class WalletSendingTest extends BitcoinSWalletTest {
}
it should "correctly make a hashed OP_RETURN commitment" in { fundedWallet =>
testOpReturnCommitment(fundedWallet.wallet, hashMessage = true)
testOpReturnCommitment(fundedWallet.wallet, hashMessage = true)(
fundedWallet.walletConfig)
}
it should "correctly make an unhashed OP_RETURN commitment" in {
fundedWallet =>
testOpReturnCommitment(fundedWallet.wallet, hashMessage = false)
testOpReturnCommitment(fundedWallet.wallet, hashMessage = false)(
fundedWallet.walletConfig)
}
it should "fail to make an OP_RETURN commitment that is too long" in {
@ -210,8 +214,10 @@ class WalletSendingTest extends BitcoinSWalletTest {
it should "correctly send from outpoints" in { fundedWallet =>
val wallet = fundedWallet.wallet
val spendingInfoDAO =
SpendingInfoDAO()(executionContext, fundedWallet.walletConfig)
for {
allOutPoints <- wallet.spendingInfoDAO.findAllOutpoints()
allOutPoints <- spendingInfoDAO.findAllOutpoints()
// use half of them
outPoints = allOutPoints.drop(allOutPoints.size / 2)
tx <- wallet.sendFundsHandling.sendFromOutPoints(outPoints,
@ -346,9 +352,10 @@ class WalletSendingTest extends BitcoinSWalletTest {
newFeeRate = SatoshisPerByte(feeRate.currencyUnit + Satoshis(50))
bumpedTx <- wallet.sendFundsHandling.bumpFeeRBF(tx.txIdBE, newFeeRate)
txDb1Opt <- wallet.outgoingTxDAO.findByTxId(tx.txIdBE)
txDb2Opt <- wallet.outgoingTxDAO.findByTxId(bumpedTx.txIdBE)
outgoingTxDAO = OutgoingTransactionDAO()(executionContext,
fundedWallet.walletConfig)
txDb1Opt <- outgoingTxDAO.findByTxId(tx.txIdBE)
txDb2Opt <- outgoingTxDAO.findByTxId(bumpedTx.txIdBE)
secondBal <- wallet.getBalance()
} yield {
@ -422,8 +429,9 @@ class WalletSendingTest extends BitcoinSWalletTest {
None)
bumpRate <- wallet.feeRateApi.getFeeRate()
child <- wallet.sendFundsHandling.bumpFeeCPFP(parent.txIdBE, bumpRate)
received <- wallet.spendingInfoDAO.findTx(child).map(_.nonEmpty)
spendingInfoDAO = SpendingInfoDAO()(executionContext,
fundedWallet.walletConfig)
received <- spendingInfoDAO.findTx(child).map(_.nonEmpty)
} yield {
// Verify we are only sending to ourself
assert(child.outputs.size == 1)
@ -492,7 +500,9 @@ class WalletSendingTest extends BitcoinSWalletTest {
DoubleSha256DigestBE.empty
) // dummy spending txid
.copyWithState(TxoState.PendingConfirmationsSpent)
_ <- wallet.spendingInfoDAO.update(spent)
spendingInfoDAO = SpendingInfoDAO()(executionContext,
fundedWallet.walletConfig)
_ <- spendingInfoDAO.update(spent)
test <- recoverToSucceededIf[IllegalArgumentException](
wallet.sendFundsHandling.sendFromOutPoints(
allUtxos.map(_.outPoint),
@ -505,7 +515,7 @@ class WalletSendingTest extends BitcoinSWalletTest {
}
def testSendWithAlgo(
wallet: Wallet,
wallet: WalletApi,
algo: CoinSelectionAlgo
): Future[Assertion] = {
for {

View File

@ -33,13 +33,17 @@ import java.util.concurrent.TimeUnit
import scala.concurrent.{ExecutionContext, Future}
import scala.util.control.NonFatal
abstract class Wallet extends NeutrinoHDWalletApi with WalletLogger {
case class Wallet(
override val nodeApi: NodeApi,
override val chainQueryApi: ChainQueryApi
)(implicit
val walletConfig: WalletAppConfig
) extends NeutrinoHDWalletApi
with WalletLogger {
def keyManager: BIP39KeyManager = {
walletConfig.kmConf.toBip39KeyManager
}
def feeRateApi: FeeRateApi = walletConfig.feeRateApi
implicit val walletConfig: WalletAppConfig
implicit val system: ActorSystem = walletConfig.system
implicit val ec: ExecutionContext = system.dispatcher
@ -50,36 +54,28 @@ abstract class Wallet extends NeutrinoHDWalletApi with WalletLogger {
val networkParameters: BitcoinNetwork = walletConfig.network
private[bitcoins] val addressDAO: AddressDAO = AddressDAO()
private[bitcoins] val accountDAO: AccountDAO = AccountDAO()
private[bitcoins] val spendingInfoDAO: SpendingInfoDAO = SpendingInfoDAO()
private[bitcoins] val transactionDAO: TransactionDAO = TransactionDAO()
private[bitcoins] val scriptPubKeyDAO: ScriptPubKeyDAO = ScriptPubKeyDAO()
private[bitcoins] val walletDAOs: WalletDAOs =
WalletDAOs.fromWalletConfig(walletConfig)
private[bitcoins] val addressDAO: AddressDAO = walletDAOs.addressDAO
private[bitcoins] val accountDAO: AccountDAO = walletDAOs.accountDAO
private[bitcoins] val spendingInfoDAO: SpendingInfoDAO = walletDAOs.utxoDAO
private[bitcoins] val transactionDAO: TransactionDAO =
walletDAOs.transactionDAO
private[bitcoins] val scriptPubKeyDAO: ScriptPubKeyDAO =
walletDAOs.scriptPubKeyDAO
private[bitcoins] val incomingTxDAO: IncomingTransactionDAO =
IncomingTransactionDAO()
walletDAOs.incomingTxDAO
private[bitcoins] val outgoingTxDAO: OutgoingTransactionDAO =
OutgoingTransactionDAO()
private[bitcoins] val addressTagDAO: AddressTagDAO = AddressTagDAO()
walletDAOs.outgoingTxDAO
private[bitcoins] val addressTagDAO: AddressTagDAO = walletDAOs.addressTagDAO
private[bitcoins] val stateDescriptorDAO: WalletStateDescriptorDAO =
WalletStateDescriptorDAO()
private def walletDAOs: WalletDAOs = WalletDAOs(accountDAO,
addressDAO,
addressTagDAO,
spendingInfoDAO,
transactionDAO,
incomingTxDAO,
outgoingTxDAO,
scriptPubKeyDAO,
stateDescriptorDAO)
walletDAOs.stateDescriptorDAO
protected lazy val safeDatabase: SafeDatabase = spendingInfoDAO.safeDatabase
val nodeApi: NodeApi
val chainQueryApi: ChainQueryApi
val creationTime: Instant = keyManager.creationTime
def utxoHandling: UtxoHandling =
@ -310,23 +306,8 @@ abstract class Wallet extends NeutrinoHDWalletApi with WalletLogger {
}
}
// todo: create multiple wallets, need to maintain multiple databases
object Wallet extends WalletLogger {
private case class WalletImpl(
nodeApi: NodeApi,
chainQueryApi: ChainQueryApi
)(implicit
val walletConfig: WalletAppConfig
) extends Wallet
def apply(
nodeApi: NodeApi,
chainQueryApi: ChainQueryApi
)(implicit config: WalletAppConfig): Wallet = {
WalletImpl(nodeApi, chainQueryApi)
}
/** Creates the master xpub for the key manager in the database
* @throws RuntimeException
* if a different master xpub key exists in the database

View File

@ -2,12 +2,11 @@ package org.bitcoins.wallet
import org.bitcoins.commons.util.BitcoinSLogger
import org.bitcoins.core.api.chain.ChainQueryApi
import org.bitcoins.core.api.dlc.wallet.DLCNeutrinoHDWalletApi
import org.bitcoins.core.api.dlc.wallet.db.{
DLCContactDb,
DLCDb,
IncomingDLCOfferDb
import org.bitcoins.core.api.dlc.wallet.{
DLCNeutrinoHDWalletApi,
IncomingDLCOfferHandlingApi
}
import org.bitcoins.core.api.dlc.wallet.db.DLCDb
import org.bitcoins.core.api.feeprovider.FeeRateApi
import org.bitcoins.core.api.node.NodeApi
import org.bitcoins.core.api.wallet.*
@ -38,7 +37,7 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
@volatile private var walletOpt: Option[DLCNeutrinoHDWalletApi] =
initWalletOpt
private def wallet: DLCNeutrinoHDWalletApi = synchronized {
override protected def walletApi: DLCNeutrinoHDWalletApi = synchronized {
walletOpt match {
case Some(wallet) => wallet
case None =>
@ -46,22 +45,25 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
}
}
override def accountHandling: AccountHandlingApi = wallet.accountHandling
override def incomingOfferHandling: IncomingDLCOfferHandlingApi =
walletApi.incomingOfferHandling
override def rescanHandling: RescanHandlingApi = wallet.rescanHandling
override def accountHandling: AccountHandlingApi = walletApi.accountHandling
override def rescanHandling: RescanHandlingApi = walletApi.rescanHandling
override def fundTxHandling: FundTransactionHandlingApi =
wallet.fundTxHandling
walletApi.fundTxHandling
override def utxoHandling: UtxoHandlingApi = wallet.utxoHandling
override def utxoHandling: UtxoHandlingApi = walletApi.utxoHandling
override def addressHandling: AddressHandlingApi = wallet.addressHandling
override def addressHandling: AddressHandlingApi = walletApi.addressHandling
override def transactionProcessing: TransactionProcessingApi =
wallet.transactionProcessing
walletApi.transactionProcessing
override def sendFundsHandling: SendFundsHandlingApi =
wallet.sendFundsHandling
walletApi.sendFundsHandling
def isInitialized: Boolean = synchronized {
walletOpt.isDefined
}
@ -76,7 +78,7 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
private def delegate[T]
: (DLCNeutrinoHDWalletApi => Future[T]) => Future[T] = {
Future(wallet).flatMap[T](_)
Future(walletApi).flatMap[T](_)
}
override def getNewAddress(): Future[BitcoinAddress] = delegate(
@ -93,10 +95,10 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
override def isRescanning(): Future[Boolean] = delegate(_.isRescanning())
override lazy val nodeApi: NodeApi = wallet.nodeApi
override lazy val chainQueryApi: ChainQueryApi = wallet.chainQueryApi
override lazy val feeRateApi: FeeRateApi = wallet.feeRateApi
override lazy val creationTime: Instant = wallet.creationTime
override lazy val nodeApi: NodeApi = walletApi.nodeApi
override lazy val chainQueryApi: ChainQueryApi = walletApi.chainQueryApi
override lazy val feeRateApi: FeeRateApi = walletApi.feeRateApi
override lazy val creationTime: Instant = walletApi.creationTime
override def getConfirmedBalance(): Future[CurrencyUnit] = delegate(
_.getConfirmedBalance()
@ -231,48 +233,6 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
_.getWalletAccounting()
)
override def registerIncomingDLCOffer(
offerTLV: DLCOfferTLV,
peer: Option[String],
message: Option[String]
): Future[Sha256Digest] = delegate(
_.registerIncomingDLCOffer(offerTLV, peer, message)
)
override def listIncomingDLCOffers(): Future[Vector[IncomingDLCOfferDb]] =
delegate(_.listIncomingDLCOffers())
override def rejectIncomingDLCOffer(offerHash: Sha256Digest): Future[Unit] =
delegate(_.rejectIncomingDLCOffer(offerHash))
override def findIncomingDLCOffer(
offerHash: Sha256Digest
): Future[Option[IncomingDLCOfferDb]] = delegate(
_.findIncomingDLCOffer(offerHash)
)
override def listDLCContacts(): Future[Vector[DLCContactDb]] = delegate(
_.listDLCContacts()
)
override def addDLCContact(contact: DLCContactDb): Future[Unit] = delegate(
_.addDLCContact(contact)
)
override def removeDLCContact(address: InetSocketAddress): Future[Unit] =
delegate(_.removeDLCContact(address))
override def findDLCContacts(alias: String): Future[Vector[DLCContactDb]] =
delegate(_.findDLCContacts(alias))
override def addDLCContactMapping(
dlcId: Sha256Digest,
contactId: InetSocketAddress
): Future[Unit] = delegate(_.addDLCContactMapping(dlcId, contactId))
override def removeDLCContactMapping(dlcId: Sha256Digest): Future[Unit] =
delegate(_.removeDLCContactMapping(dlcId))
override def listDLCsByContact(
address: InetSocketAddress
): Future[Vector[DLCStatus]] = delegate(_.listDLCsByContact(address))

View File

@ -345,7 +345,7 @@ case class WalletAppConfig(
def createHDWallet(
nodeApi: NodeApi,
chainQueryApi: ChainQueryApi
)(implicit system: ActorSystem): Future[Wallet] = {
): Future[Wallet] = {
WalletAppConfig.createHDWallet(
nodeApi = nodeApi,
chainQueryApi = chainQueryApi

View File

@ -50,22 +50,6 @@ case class AddressHandling(
private val scriptPubKeyDAO: ScriptPubKeyDAO = walletDAOs.scriptPubKeyDAO
private val networkParameters: NetworkParameters = walletConfig.network
def contains(
address: BitcoinAddress,
accountOpt: Option[(AccountHandlingApi, HDAccount)]
): Future[Boolean] = {
val possibleAddressesF = accountOpt match {
case Some((ah, account)) =>
ah.listAddresses(account)
case None =>
listAddresses()
}
possibleAddressesF.map { possibleAddresses =>
possibleAddresses.exists(_.address == address)
}
}
override def listAddresses(): Future[Vector[AddressDb]] =
addressDAO.findAllAddresses()
@ -241,7 +225,7 @@ case class AddressHandling(
}
/** @inheritdoc */
def getUnusedAddress: Future[BitcoinAddress] = {
override def getUnusedAddress: Future[BitcoinAddress] = {
for {
account <- accountHandling.getDefaultAccount()
addresses <- addressDAO.getUnusedAddresses(account.hdAccount)

View File

@ -1,5 +1,7 @@
package org.bitcoins.wallet.models
import org.bitcoins.wallet.config.WalletAppConfig
case class WalletDAOs(
accountDAO: AccountDAO,
addressDAO: AddressDAO,
@ -24,3 +26,36 @@ case class WalletDAOs(
stateDescriptorDAO
)
}
object WalletDAOs {
def fromWalletConfig(walletConfig: WalletAppConfig): WalletDAOs = {
val addressDAO: AddressDAO = AddressDAO()(walletConfig.ec, walletConfig)
val accountDAO: AccountDAO = AccountDAO()(walletConfig.ec, walletConfig)
val spendingInfoDAO: SpendingInfoDAO =
SpendingInfoDAO()(walletConfig.ec, walletConfig)
val transactionDAO: TransactionDAO =
TransactionDAO()(walletConfig.ec, walletConfig)
val scriptPubKeyDAO: ScriptPubKeyDAO =
ScriptPubKeyDAO()(walletConfig.ec, walletConfig)
val incomingTxDAO: IncomingTransactionDAO =
IncomingTransactionDAO()(walletConfig.ec, walletConfig)
val outgoingTxDAO: OutgoingTransactionDAO =
OutgoingTransactionDAO()(walletConfig.ec, walletConfig)
val addressTagDAO: AddressTagDAO =
AddressTagDAO()(walletConfig.ec, walletConfig)
val stateDescriptorDAO: WalletStateDescriptorDAO =
WalletStateDescriptorDAO()(walletConfig.ec, walletConfig)
WalletDAOs(accountDAO,
addressDAO,
addressTagDAO,
spendingInfoDAO,
transactionDAO,
incomingTxDAO,
outgoingTxDAO,
scriptPubKeyDAO,
stateDescriptorDAO)
}
}