From 668ab21ca11608c3e10dbd8977dce8a76156d590 Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Wed, 9 Mar 2022 15:36:44 -0600 Subject: [PATCH] 2022 03 09 label refactor (#4175) * Rename existing getaddresslabels -> getaddresslabel * Fix missing rename of GetAddressLabel * Modify pk constraint on wallet_address_tags to be tag_name rather than tag_type * Add dropaddresslabel for a specific address * Fix migrations * Add unit tests and fix existing tests * Add docs --- .../scala/org/bitcoins/cli/ConsoleCli.scala | 50 ++++++++++++--- .../org/bitcoins/server/RoutesSpec.scala | 39 +++++++++++- .../bitcoins/server/ServerJsonModels.scala | 27 ++++++-- .../org/bitcoins/server/WalletRoutes.scala | 61 ++++++++++++++----- .../bitcoins/core/api/wallet/WalletApi.scala | 13 +++- .../org/bitcoins/db/DbManagementTest.scala | 4 +- docs/applications/server.md | 10 ++- .../bitcoins/wallet/AddressHandlingTest.scala | 2 +- .../bitcoins/wallet/AddressLabelTest.scala | 27 ++++++-- .../wallet/models/AddressTagDAOTest.scala | 25 ++++++++ .../V16__add_tag_name_constraint.sql | 4 ++ .../V15__add_tag_name_constraint.sql | 7 +++ .../wallet/internal/AddressHandling.scala | 16 ++++- .../wallet/models/AddressTagDAO.scala | 12 +++- 14 files changed, 254 insertions(+), 43 deletions(-) create mode 100644 wallet/src/main/resources/postgresql/wallet/migration/V16__add_tag_name_constraint.sql create mode 100644 wallet/src/main/resources/sqlite/wallet/migration/V15__add_tag_name_constraint.sql diff --git a/app/cli/src/main/scala/org/bitcoins/cli/ConsoleCli.scala b/app/cli/src/main/scala/org/bitcoins/cli/ConsoleCli.scala index a05ddb0df0..d17e4678e9 100644 --- a/app/cli/src/main/scala/org/bitcoins/cli/ConsoleCli.scala +++ b/app/cli/src/main/scala/org/bitcoins/cli/ConsoleCli.scala @@ -320,8 +320,8 @@ object ConsoleCli { case other => other })) ), - cmd("getaddresslabels") - .action((_, conf) => conf.copy(command = GetAddressLabels(null))) + cmd("getaddresslabel") + .action((_, conf) => conf.copy(command = GetAddressLabel(null))) .text("Get all the labels associated with this address") .children( arg[BitcoinAddress]("address") @@ -329,14 +329,17 @@ object ConsoleCli { .required() .action((addr, conf) => conf.copy(command = conf.command match { - case getAddressLabels: GetAddressLabels => + case getAddressLabels: GetAddressLabel => getAddressLabels.copy(address = addr) case other => other })) ), + cmd("getaddresslabels") + .action((_, conf) => conf.copy(command = GetAddressLabels)) + .text("Returns all labels in wallet"), cmd("dropaddresslabels") .action((_, conf) => conf.copy(command = DropAddressLabels(null))) - .text("Drop all the labels associated with this address") + .text("Drop the label associated with the address") .children( arg[BitcoinAddress]("address") .text("The address to drop the associated labels of") @@ -348,6 +351,29 @@ object ConsoleCli { case other => other })) ), + cmd("dropaddresslabel") + .action((_, conf) => conf.copy(command = DropAddressLabel(null, null))) + .text("Drop all the labels associated with this address") + .children( + arg[BitcoinAddress]("address") + .text("The address to drop the associated labels of") + .required() + .action((addr, conf) => + conf.copy(command = conf.command match { + case dropAddressLabel: DropAddressLabel => + dropAddressLabel.copy(address = addr) + case other => other + })), + arg[String]("label") + .text("The label to drop") + .required() + .action((label, conf) => + conf.copy(command = conf.command match { + case dropAddressLabel: DropAddressLabel => + dropAddressLabel.copy(label = label) + case other => other + })) + ), cmd("sendtoaddress") .action( // TODO how to handle null here? @@ -1852,8 +1878,13 @@ object ConsoleCli { Seq(up.writeJs(address), up.writeJs(label))) case GetAddressTags(address) => RequestParam("getaddresstags", Seq(up.writeJs(address))) - case GetAddressLabels(address) => - RequestParam("getaddresslabels", Seq(up.writeJs(address))) + case GetAddressLabel(address) => + RequestParam("getaddresslabel", Seq(up.writeJs(address))) + case GetAddressLabels => + RequestParam("getaddresslabels") + case DropAddressLabel(address, label) => + RequestParam("dropaddresslabel", + Seq(up.writeJs(address), ujson.Str(label))) case DropAddressLabels(address) => RequestParam("dropaddresslabels", Seq(up.writeJs(address))) case Rescan(addressBatchSize, @@ -2355,7 +2386,12 @@ object CliCommand { case class GetAddressTags(address: BitcoinAddress) extends AppServerCliCommand - case class GetAddressLabels(address: BitcoinAddress) + case class GetAddressLabel(address: BitcoinAddress) + extends AppServerCliCommand + + case object GetAddressLabels extends AppServerCliCommand + + case class DropAddressLabel(address: BitcoinAddress, label: String) extends AppServerCliCommand case class DropAddressLabels(address: BitcoinAddress) diff --git a/app/server-test/src/test/scala/org/bitcoins/server/RoutesSpec.scala b/app/server-test/src/test/scala/org/bitcoins/server/RoutesSpec.scala index 070740681d..4ef1e49363 100644 --- a/app/server-test/src/test/scala/org/bitcoins/server/RoutesSpec.scala +++ b/app/server-test/src/test/scala/org/bitcoins/server/RoutesSpec.scala @@ -744,7 +744,7 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory { } } - "get address labels" in { + "get address label" in { (mockWalletApi .getAddressTags(_: BitcoinAddress, _: AddressTagType)) .expects(testAddress, AddressLabelTagType) @@ -753,7 +753,7 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory { val route = walletRoutes.handleCommand( - ServerCommand("getaddresslabels", Arr(Str(testAddressStr)))) + ServerCommand("getaddresslabel", Arr(Str(testAddressStr)))) Get() ~> route ~> check { assert(contentType == `application/json`) @@ -762,6 +762,41 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory { } } + "get address labels" in { + (mockWalletApi.getAddressTags: () => Future[Vector[AddressTagDb]]) + .expects() + .returning( + Future.successful(Vector(AddressTagDb(testAddress, testLabel)))) + + val route = + walletRoutes.handleCommand(ServerCommand("getaddresslabels", Arr())) + + Get() ~> route ~> check { + assert(contentType == `application/json`) + assert( + responseAs[String] == """{"result":[{"address":"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","labels":["test"]}],"error":null}""") + } + } + + "drop address label" in { + val labelName = "label" + (mockWalletApi + .dropAddressTagName(_: BitcoinAddress, _: AddressTagName)) + .expects(testAddress, AddressLabelTagName(labelName)) + .returning(Future.successful(1)) + + val route = + walletRoutes.handleCommand( + ServerCommand("dropaddresslabel", + Arr(Str(testAddressStr), Str(labelName)))) + + Get() ~> route ~> check { + assert(contentType == `application/json`) + assert( + responseAs[String] == """{"result":"""" + "1 label dropped" + """","error":null}""") + } + } + "drop address labels with no labels" in { (mockWalletApi .dropAddressTagType(_: BitcoinAddress, _: AddressTagType)) diff --git a/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala b/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala index 300a245067..9252ec4db6 100644 --- a/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala +++ b/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala @@ -114,17 +114,17 @@ object GetAddressTags extends ServerJsonModels { } } -case class GetAddressLabels(address: BitcoinAddress) +case class GetAddressLabel(address: BitcoinAddress) -object GetAddressLabels extends ServerJsonModels { +object GetAddressLabel extends ServerJsonModels { - def fromJsArr(jsArr: ujson.Arr): Try[GetAddressLabels] = { + def fromJsArr(jsArr: ujson.Arr): Try[GetAddressLabel] = { jsArr.arr.toList match { case addrJs :: Nil => Try { val addr = jsToBitcoinAddress(addrJs) - GetAddressLabels(addr) + GetAddressLabel(addr) } case other => Failure( @@ -134,6 +134,25 @@ object GetAddressLabels extends ServerJsonModels { } } +case class DropAddressLabel(address: BitcoinAddress, label: String) + +object DropAddressLabel extends ServerJsonModels { + + def fromJsArr(jsonArr: ujson.Arr): Try[DropAddressLabel] = { + jsonArr.arr.toList match { + case address :: label :: Nil => + Try { + val addr = jsToBitcoinAddress(address) + DropAddressLabel(addr, label.str) + } + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 2")) + } + } +} + case class DropAddressLabels(address: BitcoinAddress) object DropAddressLabels extends ServerJsonModels { diff --git a/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala b/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala index 221d4b903e..16d206570b 100644 --- a/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala +++ b/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala @@ -1,6 +1,7 @@ package org.bitcoins.server import akka.actor.ActorSystem +import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ import akka.stream.Materializer @@ -12,7 +13,11 @@ import org.bitcoins.core.currency._ import org.bitcoins.core.protocol.tlv._ import org.bitcoins.core.protocol.transaction.Transaction import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte -import org.bitcoins.core.wallet.utxo.{AddressLabelTagType, TxoState} +import org.bitcoins.core.wallet.utxo.{ + AddressLabelTagName, + AddressLabelTagType, + TxoState +} import org.bitcoins.crypto.NetworkElement import org.bitcoins.keymanager._ import org.bitcoins.keymanager.config.KeyManagerAppConfig @@ -228,9 +233,9 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit } } - case ServerCommand("getaddresslabels", arr) => - withValidServerCommand(GetAddressLabels.fromJsArr(arr)) { - case GetAddressLabels(address) => + case ServerCommand("getaddresslabel", arr) => + withValidServerCommand(GetAddressLabel.fromJsArr(arr)) { + case GetAddressLabel(address) => complete { wallet.getAddressTags(address, AddressLabelTagType).map { tagDbs => val retStr = tagDbs.map(_.tagName.name) @@ -239,20 +244,38 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit } } + case ServerCommand("getaddresslabels", _) => + complete { + val allTagsF = wallet.getAddressTags() + for { + allTags <- allTagsF + grouped = allTags.groupBy(_.address) + } yield { + val json: Vector[ujson.Obj] = grouped.map { case (address, labels) => + val tagNames: Vector[ujson.Str] = + labels.map(l => ujson.Str(l.tagName.name)) + ujson.Obj(("address", address.toString), + ("labels", ujson.Arr.from(tagNames))) + }.toVector + Server.httpSuccess(ujson.Arr.from(json)) + } + } + case ServerCommand("dropaddresslabel", arr) => + withValidServerCommand(DropAddressLabel.fromJsArr(arr)) { + case DropAddressLabel(address, label) => + complete { + val tagName = AddressLabelTagName(label) + val droppedF = wallet.dropAddressTagName(address, tagName) + droppedF.map(handleTagResponse) + } + } case ServerCommand("dropaddresslabels", arr) => withValidServerCommand(DropAddressLabels.fromJsArr(arr)) { case DropAddressLabels(address) => complete { - wallet.dropAddressTagType(address, AddressLabelTagType).map { - numDropped => - if (numDropped <= 0) { - Server.httpSuccess(s"Address had no labels") - } else if (numDropped == 1) { - Server.httpSuccess(s"$numDropped label dropped") - } else { - Server.httpSuccess(s"$numDropped labels dropped") - } - } + val droppedF = + wallet.dropAddressTagType(address, AddressLabelTagType) + droppedF.map(handleTagResponse) } } @@ -898,4 +921,14 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit Bitcoins(currencyUnit.satoshis).toBigDecimal.toDouble } } + + private def handleTagResponse(numDropped: Int): HttpEntity.Strict = { + if (numDropped <= 0) { + Server.httpSuccess(s"Address had no labels") + } else if (numDropped == 1) { + Server.httpSuccess(s"$numDropped label dropped") + } else { + Server.httpSuccess(s"$numDropped labels dropped") + } + } } diff --git a/core/src/main/scala/org/bitcoins/core/api/wallet/WalletApi.scala b/core/src/main/scala/org/bitcoins/core/api/wallet/WalletApi.scala index 90fff00414..6aec2b2875 100644 --- a/core/src/main/scala/org/bitcoins/core/api/wallet/WalletApi.scala +++ b/core/src/main/scala/org/bitcoins/core/api/wallet/WalletApi.scala @@ -17,7 +17,12 @@ import org.bitcoins.core.protocol.transaction.{ } import org.bitcoins.core.util.{FutureUtil, StartStopAsync} import org.bitcoins.core.wallet.fee.FeeUnit -import org.bitcoins.core.wallet.utxo.{AddressTag, AddressTagType, TxoState} +import org.bitcoins.core.wallet.utxo.{ + AddressTag, + AddressTagName, + AddressTagType, + TxoState +} import org.bitcoins.crypto.DoubleSha256DigestBE import java.time.Instant @@ -218,7 +223,7 @@ trait WalletApi extends StartStopAsync[WalletApi] { address: BitcoinAddress, tagType: AddressTagType): Future[Vector[AddressTagDb]] - def getAddressTags: Future[Vector[AddressTagDb]] + def getAddressTags(): Future[Vector[AddressTagDb]] def getAddressTags(tagType: AddressTagType): Future[Vector[AddressTagDb]] @@ -230,6 +235,10 @@ trait WalletApi extends StartStopAsync[WalletApi] { address: BitcoinAddress, addressTagType: AddressTagType): Future[Int] + def dropAddressTagName( + address: BitcoinAddress, + tagName: AddressTagName): Future[Int] + /** Generates a new change address */ protected[wallet] def getNewChangeAddress()(implicit ec: ExecutionContext): Future[BitcoinAddress] diff --git a/db-commons-test/src/test/scala/org/bitcoins/db/DbManagementTest.scala b/db-commons-test/src/test/scala/org/bitcoins/db/DbManagementTest.scala index ae5f071acf..00e90d3f98 100644 --- a/db-commons-test/src/test/scala/org/bitcoins/db/DbManagementTest.scala +++ b/db-commons-test/src/test/scala/org/bitcoins/db/DbManagementTest.scala @@ -106,13 +106,13 @@ class DbManagementTest extends BitcoinSAsyncTest with EmbeddedPg { val result = walletDbManagement.migrate() walletAppConfig.driver match { case SQLite => - val expected = 14 + val expected = 15 assert(result == expected) val flywayInfo = walletDbManagement.info() assert(flywayInfo.applied().length == expected) assert(flywayInfo.pending().length == 0) case PostgreSQL => - val expected = 12 + val expected = 13 assert(result == expected) val flywayInfo = walletDbManagement.info() diff --git a/docs/applications/server.md b/docs/applications/server.md index d00057495f..5b9d71cf61 100644 --- a/docs/applications/server.md +++ b/docs/applications/server.md @@ -306,9 +306,13 @@ the `-p 9999:9999` port mapping on the docker container to adjust for this. - `message` - Peer's message or note (optional) - `"offer-remove` `hash` - Remove an incoming offer from inbox - `hash` - Hash of the offer TLV -- `offer-send` `offerOrTempContractId` `peerAddress` `message` - Sends an offer to a peer. `offerOrTempContractId` is either an offer TLV or a temporary contract ID. -- `offers-list` - List all incoming offers from the inbox -- `getdlcoffer` `tempContractId` - Gets a DLC offer by temporary contract ID. + - `offer-send` `offerOrTempContractId` `peerAddress` `message` - Sends an offer to a peer. `offerOrTempContractId` is either an offer TLV or a temporary contract ID. + - `offers-list` - List all incoming offers from the inbox + - `getdlcoffer` `tempContractId` - Gets a DLC offer by temporary contract ID. + - `getaddresslabel` `address` - gets all labels for an address + - `getaddresslabels` - returns all addresses with labels in the wallet + - `dropaddresslabel` `address` `label` - drops the label for a given address + - `dropaddresslabels` `address` - drops all labels for the given address ### Network - `getpeers` - List the connected peers diff --git a/wallet-test/src/test/scala/org/bitcoins/wallet/AddressHandlingTest.scala b/wallet-test/src/test/scala/org/bitcoins/wallet/AddressHandlingTest.scala index 6f2b29ebd7..6af719fe9a 100644 --- a/wallet-test/src/test/scala/org/bitcoins/wallet/AddressHandlingTest.scala +++ b/wallet-test/src/test/scala/org/bitcoins/wallet/AddressHandlingTest.scala @@ -283,7 +283,7 @@ class AddressHandlingTest extends BitcoinSWalletTest { for { _ <- addressF _ <- wallet.clearAllUtxosAndAddresses() - tags <- wallet.getAddressTags + tags <- wallet.getAddressTags() } yield { assert(tags.isEmpty) } diff --git a/wallet-test/src/test/scala/org/bitcoins/wallet/AddressLabelTest.scala b/wallet-test/src/test/scala/org/bitcoins/wallet/AddressLabelTest.scala index e62444171a..7381d62a23 100644 --- a/wallet-test/src/test/scala/org/bitcoins/wallet/AddressLabelTest.scala +++ b/wallet-test/src/test/scala/org/bitcoins/wallet/AddressLabelTest.scala @@ -5,6 +5,8 @@ import org.bitcoins.testkit.wallet.BitcoinSWalletTest import org.bitcoins.testkit.wallet.FundWalletUtil.FundedWallet import org.scalatest.FutureOutcome +import java.sql.SQLException + class AddressLabelTest extends BitcoinSWalletTest { type FixtureParam = FundedWallet @@ -14,18 +16,18 @@ class AddressLabelTest extends BitcoinSWalletTest { behavior of "Address tags" - it must "add two labels to the database" in { fundedWallet => + it must "add two tags to the database" in { fundedWallet => val wallet = fundedWallet.wallet val tag1 = UnknownAddressTag("test_tag_name_1", "test_tag_type_1") val tag2 = UnknownAddressTag("test_tag_name_2", "test_tag_type_2") val addressF = for { address <- wallet.getNewAddress(Vector(tag1)) //add another tag to address - tagDb1 <- wallet.tagAddress(address, tag1) + tagDb1 <- wallet.getAddressTags(address) tagDb2 <- wallet.tagAddress(address, tag2) } yield { - assert(tagDb1.address == address) - assert(tagDb1.tagName == tag1.tagName) + assert(tagDb1.head.address == address) + assert(tagDb1.head.tagName == tag1.tagName) assert(tagDb2.tagName == tag2.tagName) assert(tagDb2.address == address) @@ -33,4 +35,21 @@ class AddressLabelTest extends BitcoinSWalletTest { addressF } + + it must "fail if we tag the address with the same tag twice" in { + fundedWallet => + val wallet = fundedWallet.wallet + val tag1 = UnknownAddressTag(tagName = "test_tag_name_1", + tagType = "test_tag_type_1") + val tag2 = UnknownAddressTag(tagName = "test_tag_name_1", + tagType = "test_tag_type_2") + val resultF = for { + address <- wallet.getNewAddress() + //add another tag to address + _ <- wallet.tagAddress(address, tag1) + _ <- wallet.tagAddress(address, tag2) + } yield () + + recoverToSucceededIf[SQLException](resultF) + } } diff --git a/wallet-test/src/test/scala/org/bitcoins/wallet/models/AddressTagDAOTest.scala b/wallet-test/src/test/scala/org/bitcoins/wallet/models/AddressTagDAOTest.scala index 7e6a915ea8..f0fae9df2e 100644 --- a/wallet-test/src/test/scala/org/bitcoins/wallet/models/AddressTagDAOTest.scala +++ b/wallet-test/src/test/scala/org/bitcoins/wallet/models/AddressTagDAOTest.scala @@ -83,4 +83,29 @@ class AddressTagDAOTest extends WalletDAOFixture { daos => testInsertion(daos, HotStorage) } + + it must "delete a tag by name" in { daos => + val accountDAO = daos.accountDAO + val addressDAO = daos.addressDAO + val addressTagDAO = daos.addressTagDAO + for { + createdAccount <- { + val account = WalletTestUtil.firstAccountDb + accountDAO.create(account) + } + createdAddress <- { + val addressDb = WalletTestUtil.getAddressDb(createdAccount) + addressDAO.create(addressDb) + } + createdAddressTag <- { + val tagDb = + AddressTagDb(createdAddress.address, exampleTag) + addressTagDAO.create(tagDb) + } + dropped <- addressTagDAO.dropByAddressAndName(createdAddress.address, + createdAddressTag.tagName) + } yield { + assert(dropped == 1) + } + } } diff --git a/wallet/src/main/resources/postgresql/wallet/migration/V16__add_tag_name_constraint.sql b/wallet/src/main/resources/postgresql/wallet/migration/V16__add_tag_name_constraint.sql new file mode 100644 index 0000000000..dc14431aed --- /dev/null +++ b/wallet/src/main/resources/postgresql/wallet/migration/V16__add_tag_name_constraint.sql @@ -0,0 +1,4 @@ +--changes pk to (address,tag_name) rather than (address,tag_type) +ALTER TABLE wallet_address_tags DROP CONSTRAINT IF EXISTS "pk_address_tags"; + +ALTER TABLE wallet_address_tags ADD CONSTRAINT pk_address_tags PRIMARY KEY (address, tag_name); \ No newline at end of file diff --git a/wallet/src/main/resources/sqlite/wallet/migration/V15__add_tag_name_constraint.sql b/wallet/src/main/resources/sqlite/wallet/migration/V15__add_tag_name_constraint.sql new file mode 100644 index 0000000000..f4c87c8258 --- /dev/null +++ b/wallet/src/main/resources/sqlite/wallet/migration/V15__add_tag_name_constraint.sql @@ -0,0 +1,7 @@ +-- This changes the primary key to (address, tag_name) +CREATE TABLE "wallet_address_tags_temp" ("address" VARCHAR(254) NOT NULL,"tag_name" VARCHAR(254) NOT NULL,"tag_type" VARCHAR(254) NOT NULL); +INSERT INTO "wallet_address_tags_temp" SELECT "address", "tag_name", "tag_type" FROM "wallet_address_tags"; +DROP TABLE "wallet_address_tags"; +CREATE TABLE "wallet_address_tags" ("address" VARCHAR(254) NOT NULL,"tag_name" VARCHAR(254) NOT NULL,"tag_type" VARCHAR(254) NOT NULL,constraint "pk_address_tags" primary key ("address", "tag_name"), constraint "fk_address" foreign key("address") references "addresses"("address") on update NO ACTION on delete NO ACTION); +INSERT INTO "wallet_address_tags" SELECT "address", "tag_name", "tag_type" FROM "wallet_address_tags_temp"; +DROP TABLE "wallet_address_tags_temp"; \ No newline at end of file diff --git a/wallet/src/main/scala/org/bitcoins/wallet/internal/AddressHandling.scala b/wallet/src/main/scala/org/bitcoins/wallet/internal/AddressHandling.scala index 2359ab51de..8ffc3fe868 100644 --- a/wallet/src/main/scala/org/bitcoins/wallet/internal/AddressHandling.scala +++ b/wallet/src/main/scala/org/bitcoins/wallet/internal/AddressHandling.scala @@ -13,7 +13,11 @@ import org.bitcoins.core.protocol.transaction.{ TransactionOutPoint, TransactionOutput } -import org.bitcoins.core.wallet.utxo.{AddressTag, AddressTagType} +import org.bitcoins.core.wallet.utxo.{ + AddressTag, + AddressTagName, + AddressTagType +} import org.bitcoins.crypto.ECPublicKey import org.bitcoins.wallet._ @@ -414,7 +418,7 @@ private[wallet] trait AddressHandling extends WalletLogger { address: BitcoinAddress, tag: AddressTag): Future[AddressTagDb] = { val addressTagDb = AddressTagDb(address, tag) - val f = addressTagDAO.upsert(addressTagDb) + val f = addressTagDAO.create(addressTagDb) f } @@ -428,7 +432,7 @@ private[wallet] trait AddressHandling extends WalletLogger { addressTagDAO.findByAddressAndTag(address, tagType) } - def getAddressTags: Future[Vector[AddressTagDb]] = { + def getAddressTags(): Future[Vector[AddressTagDb]] = { addressTagDAO.findAll() } @@ -451,6 +455,12 @@ private[wallet] trait AddressHandling extends WalletLogger { addressTagDAO.dropByAddressAndTag(address, addressTagType) } + override def dropAddressTagName( + address: BitcoinAddress, + addressTagName: AddressTagName): Future[Int] = { + addressTagDAO.dropByAddressAndName(address, addressTagName) + } + private lazy val addressRequestQueue = { val queue = new java.util.concurrent.ArrayBlockingQueue[AddressRequest]( walletConfig.addressQueueSize diff --git a/wallet/src/main/scala/org/bitcoins/wallet/models/AddressTagDAO.scala b/wallet/src/main/scala/org/bitcoins/wallet/models/AddressTagDAO.scala index b2e55b65be..7eaec97120 100644 --- a/wallet/src/main/scala/org/bitcoins/wallet/models/AddressTagDAO.scala +++ b/wallet/src/main/scala/org/bitcoins/wallet/models/AddressTagDAO.scala @@ -125,6 +125,16 @@ case class AddressTagDAO()(implicit safeDatabase.run(query.delete) } + def dropByAddressAndName( + address: BitcoinAddress, + tagName: AddressTagName): Future[Int] = { + val query = table + .filter(_.address === address) + .filter(_.tagName === tagName) + + safeDatabase.run(query.delete) + } + def findTx( tx: Transaction, network: NetworkParameters): Future[Vector[AddressTagDb]] = { @@ -201,7 +211,7 @@ case class AddressTagDAO()(implicit (address, tagName, tagType).<>(fromTuple, toTuple) def primaryKey: PrimaryKey = - primaryKey("pk_address_tags", sourceColumns = (address, tagType)) + primaryKey("pk_address_tags", sourceColumns = (address, tagName)) /** All tags must have an associated address */ def fk_address = {