rework bitcoind-rpc to take a walletName parameter that is a String (#5545)

* rework bitcoind-rpc to take a wallet name parameter that is a String rather than Option[String]

* Fix docs
This commit is contained in:
Chris Stewart 2024-04-26 08:09:28 -05:00 committed by GitHub
parent 4915350594
commit d23d7851b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 94 additions and 93 deletions

View File

@ -3,8 +3,8 @@ package org.bitcoins.docs
import mdoc.MainSettings
import scala.meta.io.AbsolutePath
/** This is cribbed from how Bloop does
* docs generation: https://github.com/scalacenter/bloop/blob/6c8dc54b7bdf5a6145b31f94b73456693c0d1230/docs-gen/src/main/scala/bloop/Docs.scala#L8-L35
/** This is cribbed from how Bloop does docs generation:
* https://github.com/scalacenter/bloop/blob/6c8dc54b7bdf5a6145b31f94b73456693c0d1230/docs-gen/src/main/scala/bloop/Docs.scala#L8-L35
*/
object DocsGen extends App {
val cwd0 = AbsolutePath.workingDirectory

View File

@ -55,10 +55,10 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
pair
for {
_ <- walletClient.createWallet(walletName)
_ <- walletClient.encryptWallet(password, Some(walletName))
_ <- walletClient.encryptWallet(password, walletName)
_ <-
walletClient
.getNewAddress(Some(walletName))
.getNewAddress(walletName)
.flatMap(walletClient.generateToAddress(101, _))
_ <- client.createWallet(walletName)
@ -115,7 +115,7 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
}
val datadir = localInstance.datadir.getAbsolutePath
for {
_ <- walletClient.backupWallet(datadir + "/backup.dat", Some(walletName))
_ <- walletClient.backupWallet(datadir + "/backup.dat", walletName)
} yield {
val file = new File(datadir + "/backup.dat")
assert(file.exists)
@ -127,7 +127,7 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
val walletClient = nodePair.node2
for {
_ <- walletClient.walletLock(walletName)
_ <- walletClient.walletPassphrase(password, 1000, Some(walletName))
_ <- walletClient.walletPassphrase(password, 1000, walletName)
info <- walletClient.getWalletInfo(walletName)
_ = assert(info.unlocked_until.nonEmpty)
@ -169,9 +169,9 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
nodePair =>
val client = nodePair.node2
for {
address <- client.getNewAddress(Some(walletName))
address <- client.getNewAddress(walletName)
amount <-
client.getReceivedByAddress(address, walletNameOpt = Some(walletName))
client.getReceivedByAddress(address, walletName = walletName)
} yield assert(amount == Bitcoins(0))
}
@ -199,9 +199,9 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
it should "be able to refill the keypool" in { nodePair =>
val client = nodePair.node2
for {
_ <- client.walletPassphrase(password, 1000, Some(walletName))
_ <- client.walletPassphrase(password, 1000, walletName)
info <- client.getWalletInfo(walletName)
_ <- client.keyPoolRefill(info.keypoolsize + 1, Some(walletName))
_ <- client.keyPoolRefill(info.keypoolsize + 1, walletName)
newInfo <- client.getWalletInfo(walletName)
} yield assert(newInfo.keypoolsize == info.keypoolsize + 1)
}
@ -213,10 +213,10 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
for {
_ <- walletClient.walletLock(walletName)
_ <-
walletClient.walletPassphraseChange(password, newPass, Some(walletName))
walletClient.walletPassphraseChange(password, newPass, walletName)
_ = password = newPass
_ <- walletClient.walletPassphrase(password, 1000, Some(walletName))
_ <- walletClient.walletPassphrase(password, 1000, walletName)
info <- walletClient.getWalletInfo(walletName)
_ <- walletClient.walletLock(walletName)
newInfo <- walletClient.getWalletInfo(walletName)
@ -232,15 +232,15 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
val otherClient = nodePair.node1
val client = nodePair.node2
for {
address <- otherClient.getNewAddress(Some(walletName))
_ <- client.walletPassphrase(password, 1000, Some(walletName))
address <- otherClient.getNewAddress(walletName)
_ <- client.walletPassphrase(password, 1000, walletName)
txid <- client.sendToAddress(
address,
Bitcoins(1),
walletNameOpt = Some(walletName)
walletName = walletName
)
transaction <-
client.getTransaction(txid, walletNameOpt = Some(walletName))
client.getTransaction(txid, walletName = walletName)
} yield {
assert(transaction.amount == Bitcoins(-1))
assert(transaction.details.head.address.contains(address))
@ -251,17 +251,17 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
val otherClient = nodePair.node1
val client = nodePair.node2
for {
address1 <- otherClient.getNewAddress(Some(walletName))
address2 <- otherClient.getNewAddress(Some(walletName))
_ <- client.walletPassphrase(password, 1000, Some(walletName))
address1 <- otherClient.getNewAddress(walletName)
address2 <- otherClient.getNewAddress(walletName)
_ <- client.walletPassphrase(password, 1000, walletName)
txid <-
client
.sendMany(
Map(address1 -> Bitcoins(1), address2 -> Bitcoins(2)),
walletNameOpt = Some(walletName)
walletName = walletName
)
transaction <-
client.getTransaction(txid, walletNameOpt = Some(walletName))
client.getTransaction(txid, walletName = walletName)
} yield {
assert(transaction.amount == Bitcoins(-3))
assert(transaction.details.exists(_.address.contains(address1)))
@ -275,7 +275,7 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
balance <- client.getBalance(walletName)
_ <-
client
.getNewAddress(Some(walletName))
.getNewAddress(walletName)
.flatMap(client.generateToAddress(1, _))
newBalance <- client.getBalance(walletName)
} yield {
@ -318,7 +318,7 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
)
),
rescan = false,
walletNameOpt = Some(walletName)
walletName = walletName
)
} yield {
assert(secondResult.length == 2)
@ -330,7 +330,7 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
it should "be able to set the tx fee" in { nodePair =>
val client = nodePair.node2
for {
success <- client.setTxFee(Bitcoins(0.01), Some(walletName))
success <- client.setTxFee(Bitcoins(0.01), walletName)
info <- client.getWalletInfo(walletName)
} yield {
assert(success)
@ -342,7 +342,7 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
val otherClient = nodePair.node1
val client = nodePair.node2
for {
address <- otherClient.getNewAddress(Some(walletName))
address <- otherClient.getNewAddress(walletName)
transactionWithoutFunds <-
client
.createRawTransaction(Vector.empty, Map(address -> Bitcoins(1)))
@ -351,7 +351,7 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairNewest {
transaction = transactionResult.hex
singedTx <-
client
.signRawTransactionWithWallet(transaction, Some(walletName))
.signRawTransactionWithWallet(transaction, walletName)
.map(_.hex)
// Will throw error if invalid

View File

@ -75,12 +75,12 @@ trait TransactionRpc { self: Client =>
def getTransaction(
txid: DoubleSha256DigestBE,
watchOnly: Boolean = false,
walletNameOpt: Option[String] = None
walletName: String = BitcoindRpcClient.DEFAULT_WALLET_NAME
): Future[GetTransactionResult] = {
bitcoindCall[GetTransactionResult](
"gettransaction",
List(JsString(txid.hex), JsBoolean(watchOnly)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}

View File

@ -22,26 +22,26 @@ import scala.concurrent.Future
/** RPC calls related to wallet management functionality in bitcoind
*/
trait WalletRpc { self: Client =>
private val DEFAULT_WALLET = BitcoindRpcClient.DEFAULT_WALLET_NAME
def backupWallet(
destination: String,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[Unit] = {
bitcoindCall[Unit](
"backupwallet",
List(JsString(destination)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
def encryptWallet(
passphrase: String,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[String] = {
bitcoindCall[String](
"encryptwallet",
List(JsString(passphrase)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
@ -59,12 +59,12 @@ trait WalletRpc { self: Client =>
def getReceivedByAddress(
address: BitcoinAddress,
minConfirmations: Int = 1,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[Bitcoins] = {
bitcoindCall[Bitcoins](
"getreceivedbyaddress",
List(JsString(address.toString), JsNumber(minConfirmations)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
@ -82,7 +82,7 @@ trait WalletRpc { self: Client =>
private def getNewAddressInternal(
accountOrLabel: String = "",
addressType: Option[AddressType],
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[BitcoinAddress] = {
val params =
List(JsString(accountOrLabel)) ++ addressType.map(Json.toJson(_)).toList
@ -90,7 +90,7 @@ trait WalletRpc { self: Client =>
bitcoindCall[BitcoinAddress](
"getnewaddress",
params,
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
).map(addr =>
BitcoinAddress.fromScriptPubKey(addr.scriptPubKey, instance.network))
}
@ -98,20 +98,25 @@ trait WalletRpc { self: Client =>
def getNewAddress: Future[BitcoinAddress] =
getNewAddressInternal(addressType = None)
def getNewAddress(walletNameOpt: Option[String]): Future[BitcoinAddress] =
getNewAddressInternal(addressType = None, walletNameOpt = walletNameOpt)
def getNewAddress(
walletName: String,
label: String = ""): Future[BitcoinAddress] =
getNewAddressInternal(accountOrLabel = label,
addressType = None,
walletName = walletName)
def getNewAddress(
addressType: AddressType,
walletNameOpt: Option[String]): Future[BitcoinAddress] =
walletName: String): Future[BitcoinAddress] =
getNewAddressInternal(addressType = Some(addressType),
walletNameOpt = walletNameOpt)
walletName = walletName)
def getNewAddress(addressType: AddressType): Future[BitcoinAddress] =
getNewAddressInternal(addressType = Some(addressType))
def getNewAddress(accountOrLabel: String): Future[BitcoinAddress] =
getNewAddressInternal(accountOrLabel, None)
// how to di
// def getNewAddress(accountOrLabel: String): Future[BitcoinAddress] =
// getNewAddressInternal(accountOrLabel, None)
def getNewAddress(
accountOrLabel: String,
@ -124,16 +129,16 @@ trait WalletRpc { self: Client =>
addressType: AddressType,
walletName: String
): Future[BitcoinAddress] =
getNewAddressInternal(accountOrLabel, Some(addressType), Some(walletName))
getNewAddressInternal(accountOrLabel, Some(addressType), walletName)
private def getRawChangeAddressInternal(
addressType: Option[AddressType],
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[BitcoinAddress] = {
bitcoindCall[BitcoinAddress](
"getrawchangeaddress",
addressType.map(Json.toJson(_)).toList,
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
@ -144,96 +149,92 @@ trait WalletRpc { self: Client =>
getRawChangeAddressInternal(Some(addressType))
def getRawChangeAddress(walletName: String): Future[BitcoinAddress] =
getRawChangeAddressInternal(None, Some(walletName))
getRawChangeAddressInternal(None, walletName)
def getRawChangeAddress(
addressType: AddressType,
walletName: String
): Future[BitcoinAddress] =
getRawChangeAddressInternal(Some(addressType), Some(walletName))
getRawChangeAddressInternal(Some(addressType), walletName)
private def getWalletInfo(
walletName: Option[String]
def getWalletInfo(
walletName: String
): Future[GetWalletInfoResult] = {
self.version.flatMap {
case BitcoindVersion.V25 | BitcoindVersion.V24 |
BitcoindVersion.Unknown =>
bitcoindCall[GetWalletInfoResultPostV22](
"getwalletinfo",
uriExtensionOpt = walletName.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
}
def getWalletInfo: Future[GetWalletInfoResult] = {
getWalletInfo(None)
}
def getWalletInfo(walletName: String): Future[GetWalletInfoResult] = {
getWalletInfo(Some(walletName))
getWalletInfo(DEFAULT_WALLET)
}
/** @return
*/
def keyPoolRefill(
keyPoolSize: Int = 100,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[Unit] = {
bitcoindCall[Unit](
"keypoolrefill",
List(JsNumber(keyPoolSize)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
def importMulti(
requests: Vector[RpcOpts.ImportMultiRequest],
rescan: Boolean = true,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[Vector[ImportMultiResult]] = {
bitcoindCall[Vector[ImportMultiResult]](
"importmulti",
List(Json.toJson(requests), JsObject(Map("rescan" -> JsBoolean(rescan)))),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
def importPrunedFunds(
transaction: Transaction,
txOutProof: MerkleBlock,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[Unit] = {
bitcoindCall[Unit](
"importprunedfunds",
List(JsString(transaction.hex), JsString(txOutProof.hex)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
def removePrunedFunds(
txid: DoubleSha256DigestBE,
walletNameOpt: Option[String]
walletName: String
): Future[Unit] = {
bitcoindCall[Unit](
"removeprunedfunds",
List(JsString(txid.hex)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
def removePrunedFunds(txid: DoubleSha256DigestBE): Future[Unit] = {
removePrunedFunds(txid, None)
removePrunedFunds(txid, DEFAULT_WALLET)
}
def removePrunedFunds(txid: DoubleSha256Digest): Future[Unit] = {
removePrunedFunds(txid.flip, None)
removePrunedFunds(txid.flip, DEFAULT_WALLET)
}
def removePrunedFunds(
txid: DoubleSha256Digest,
walletNameOpt: Option[String]
walletName: String
): Future[Unit] = {
removePrunedFunds(txid.flip, walletNameOpt)
removePrunedFunds(txid.flip, walletName)
}
def listAddressGroupings: Future[Vector[Vector[RpcAddress]]] = {
@ -253,7 +254,7 @@ trait WalletRpc { self: Client =>
confirmations: Int = 1,
includeEmpty: Boolean = false,
includeWatchOnly: Boolean = false,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[Vector[ReceivedAddress]] = {
bitcoindCall[Vector[ReceivedAddress]](
"listreceivedbyaddress",
@ -262,7 +263,7 @@ trait WalletRpc { self: Client =>
JsBoolean(includeEmpty),
JsBoolean(includeWatchOnly)
),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
@ -283,12 +284,12 @@ trait WalletRpc { self: Client =>
def setWalletFlag(
flag: WalletFlag,
value: Boolean,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[SetWalletFlagResult] = {
bitcoindCall[SetWalletFlagResult](
"setwalletflag",
List(JsString(flag.toString), Json.toJson(value)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
@ -306,12 +307,12 @@ trait WalletRpc { self: Client =>
// TODO: Should be BitcoinFeeUnit
def setTxFee(
feePerKB: Bitcoins,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[Boolean] = {
bitcoindCall[Boolean](
"settxfee",
List(JsNumber(feePerKB.toBigDecimal)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
@ -329,42 +330,42 @@ trait WalletRpc { self: Client =>
def walletPassphrase(
passphrase: String,
seconds: Int,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[Unit] = {
bitcoindCall[Unit](
"walletpassphrase",
List(JsString(passphrase), JsNumber(seconds)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
def walletPassphraseChange(
currentPassphrase: String,
newPassphrase: String,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[Unit] = {
bitcoindCall[Unit](
"walletpassphrasechange",
List(JsString(currentPassphrase), JsString(newPassphrase)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
def signRawTransactionWithWallet(
transaction: Transaction,
walletNameOpt: Option[String]
walletName: String
): Future[SignRawTransactionWithWalletResult] = {
bitcoindCall[SignRawTransactionWithWalletResult](
"signrawtransactionwithwallet",
List(JsString(transaction.hex)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
def signRawTransactionWithWallet(
transaction: Transaction
): Future[SignRawTransactionWithWalletResult] = {
signRawTransactionWithWallet(transaction, None)
signRawTransactionWithWallet(transaction, DEFAULT_WALLET)
}
/** @param blank
@ -409,20 +410,20 @@ trait WalletRpc { self: Client =>
def getAddressInfo(
address: BitcoinAddress,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[AddressInfoResult] = {
self.version.flatMap {
case Unknown =>
bitcoindCall[AddressInfoResultPostV18](
"getaddressinfo",
List(JsString(address.value)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
case V25 | V24 =>
bitcoindCall[AddressInfoResultPostV21](
"getaddressinfo",
List(JsString(address.value)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
}
@ -432,7 +433,7 @@ trait WalletRpc { self: Client =>
minconf: Int = 1,
comment: String = "",
subtractFeeFrom: Vector[BitcoinAddress] = Vector.empty,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[DoubleSha256DigestBE] = {
val jsonOutputs: JsValue = Json.toJson {
amounts.map { case (addr, curr) =>
@ -448,7 +449,7 @@ trait WalletRpc { self: Client =>
JsString(comment),
Json.toJson(subtractFeeFrom)
),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
@ -458,7 +459,7 @@ trait WalletRpc { self: Client =>
localComment: String = "",
toComment: String = "",
subractFeeFromAmount: Boolean = false,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[DoubleSha256DigestBE] = {
bitcoindCall[DoubleSha256DigestBE](
"sendtoaddress",
@ -469,7 +470,7 @@ trait WalletRpc { self: Client =>
JsString(toComment),
JsBoolean(subractFeeFromAmount)
),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
@ -477,12 +478,12 @@ trait WalletRpc { self: Client =>
psbt: PSBT,
sign: Boolean = true,
sigHashType: HashType = HashType.sigHashAll,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[WalletProcessPsbtResult] = {
bitcoindCall[WalletProcessPsbtResult](
"walletprocesspsbt",
List(JsString(psbt.base64), JsBoolean(sign), Json.toJson(sigHashType)),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}
@ -492,7 +493,7 @@ trait WalletRpc { self: Client =>
locktime: Int = 0,
options: WalletCreateFundedPsbtOptions = WalletCreateFundedPsbtOptions(),
bip32derivs: Boolean = false,
walletNameOpt: Option[String] = None
walletName: String = DEFAULT_WALLET
): Future[WalletCreateFundedPsbtResult] = {
val jsonOutputs =
Json.toJson {
@ -507,7 +508,7 @@ trait WalletRpc { self: Client =>
Json.toJson(options),
Json.toJson(bip32derivs)
),
uriExtensionOpt = walletNameOpt.map(walletExtension)
uriExtensionOpt = Some(walletExtension(walletName))
)
}

View File

@ -75,7 +75,7 @@ val client = BitcoindRpcClient.fromDatadir(binary=new File("/path/to/bitcoind"),
for {
_ <- client.start()
_ <- client.walletPassphrase("mypassword", 10000, Some("walletName"))
_ <- client.walletPassphrase("mypassword", 10000, "walletName")
balance <- client.getBalance("walletName")
} yield balance
```