Bump Fee Cli commands (#2415)

* Bump Fee Cli commands

* Clarify fee rate, add to docs
This commit is contained in:
Ben Carman 2020-12-22 14:19:46 -06:00 committed by GitHub
parent 1e394737c6
commit 4da53d5569
5 changed files with 165 additions and 1 deletions

View file

@ -718,6 +718,56 @@ object ConsoleCli {
case other => other
}))
),
cmd("bumpfeecpfp")
.action((_, conf) =>
conf.copy(command = BumpFeeCPFP(DoubleSha256DigestBE.empty,
SatoshisPerVirtualByte.zero)))
.text("Bump the fee of the given transaction id with a child tx using the given fee rate")
.children(
arg[DoubleSha256DigestBE]("txid")
.text("Id of transaction to bump fee")
.required()
.action((txid, conf) =>
conf.copy(command = conf.command match {
case cpfp: BumpFeeCPFP =>
cpfp.copy(txId = txid)
case other => other
})),
arg[SatoshisPerVirtualByte]("feerate")
.text("Fee rate in sats per virtual byte of the child transaction")
.required()
.action((feeRate, conf) =>
conf.copy(command = conf.command match {
case cpfp: BumpFeeCPFP =>
cpfp.copy(feeRate = feeRate)
case other => other
}))
),
cmd("bumpfeerbf")
.action((_, conf) =>
conf.copy(command = BumpFeeRBF(DoubleSha256DigestBE.empty,
SatoshisPerVirtualByte.zero)))
.text("Replace given transaction with one with the new fee rate")
.children(
arg[DoubleSha256DigestBE]("txid")
.text("Id of transaction to bump fee")
.required()
.action((txid, conf) =>
conf.copy(command = conf.command match {
case rbf: BumpFeeRBF =>
rbf.copy(txId = txid)
case other => other
})),
arg[SatoshisPerVirtualByte]("feerate")
.text("New fee rate in sats per virtual byte")
.required()
.action((feeRate, conf) =>
conf.copy(command = conf.command match {
case rbf: BumpFeeRBF =>
rbf.copy(feeRate = feeRate)
case other => other
}))
),
cmd("gettransaction")
.action((_, conf) =>
conf.copy(command = GetTransaction(DoubleSha256DigestBE.empty)))
@ -1418,6 +1468,10 @@ object ConsoleCli {
up.writeJs(bitcoins),
up.writeJs(feeRateOpt),
up.writeJs(algo)))
case BumpFeeCPFP(txId, feeRate) =>
RequestParam("bumpfeecpfp", Seq(up.writeJs(txId), up.writeJs(feeRate)))
case BumpFeeRBF(txId, feeRate) =>
RequestParam("bumpfeerbf", Seq(up.writeJs(txId), up.writeJs(feeRate)))
case OpReturnCommit(message, hashMessage, satoshisPerVirtualByte) =>
RequestParam("opreturncommit",
Seq(up.writeJs(message),
@ -1715,6 +1769,16 @@ object CliCommand {
feeRateOpt: Option[SatoshisPerVirtualByte])
extends CliCommand
case class BumpFeeCPFP(
txId: DoubleSha256DigestBE,
feeRate: SatoshisPerVirtualByte)
extends CliCommand
case class BumpFeeRBF(
txId: DoubleSha256DigestBE,
feeRate: SatoshisPerVirtualByte)
extends CliCommand
case class SignPSBT(psbt: PSBT) extends CliCommand
case class LockUnspent(

View file

@ -1032,6 +1032,50 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
}
}
"bump fee with rbf" in {
(mockWalletApi
.bumpFeeRBF(_: DoubleSha256DigestBE, _: FeeUnit))
.expects(DoubleSha256DigestBE.empty, SatoshisPerVirtualByte.one)
.returning(Future.successful(EmptyTransaction))
(mockWalletApi.broadcastTransaction _)
.expects(EmptyTransaction)
.returning(FutureUtil.unit)
.anyNumberOfTimes()
val route = walletRoutes.handleCommand(
ServerCommand("bumpfeerbf",
Arr(Str(DoubleSha256DigestBE.empty.hex), Num(1))))
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(
responseAs[String] == """{"result":"0000000000000000000000000000000000000000000000000000000000000000","error":null}""")
}
}
"bump fee with CPFP" in {
(mockWalletApi
.bumpFeeCPFP(_: DoubleSha256DigestBE, _: FeeUnit))
.expects(DoubleSha256DigestBE.empty, SatoshisPerVirtualByte.one)
.returning(Future.successful(EmptyTransaction))
(mockWalletApi.broadcastTransaction _)
.expects(EmptyTransaction)
.returning(FutureUtil.unit)
.anyNumberOfTimes()
val route = walletRoutes.handleCommand(
ServerCommand("bumpfeecpfp",
Arr(Str(DoubleSha256DigestBE.empty.hex), Num(1))))
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(
responseAs[String] == """{"result":"0000000000000000000000000000000000000000000000000000000000000000","error":null}""")
}
}
"return the peer list" in {
val route =
nodeRoutes.handleCommand(ServerCommand("getpeers", Arr()))

View file

@ -689,6 +689,30 @@ object OpReturnCommit extends ServerJsonModels {
}
}
case class BumpFee(txId: DoubleSha256DigestBE, feeRate: SatoshisPerVirtualByte)
object BumpFee extends ServerJsonModels {
def fromJsArr(jsArr: ujson.Arr): Try[BumpFee] = {
jsArr.arr.toList match {
case txIdJs :: feeRateJs :: Nil =>
Try {
val txId = DoubleSha256DigestBE(txIdJs.str)
val feeRate = SatoshisPerVirtualByte(Satoshis(feeRateJs.num.toLong))
BumpFee(txId, feeRate)
}
case Nil =>
Failure(
new IllegalArgumentException("Missing txId and fee rate arguments"))
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 2"))
}
}
}
// Oracle Models
case class CreateEvent(

View file

@ -321,6 +321,32 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit
}
}
case ServerCommand("bumpfeerbf", arr) =>
BumpFee.fromJsArr(arr) match {
case Failure(exception) =>
reject(ValidationRejection("failure", Some(exception)))
case Success(BumpFee(txId, feeRate)) =>
complete {
for {
tx <- wallet.bumpFeeRBF(txId, feeRate)
_ <- wallet.broadcastTransaction(tx)
} yield Server.httpSuccess(tx.txIdBE)
}
}
case ServerCommand("bumpfeecpfp", arr) =>
BumpFee.fromJsArr(arr) match {
case Failure(exception) =>
reject(ValidationRejection("failure", Some(exception)))
case Success(BumpFee(txId, feeRate)) =>
complete {
for {
tx <- wallet.bumpFeeCPFP(txId, feeRate)
_ <- wallet.broadcastTransaction(tx)
} yield Server.httpSuccess(tx.txIdBE)
}
}
case ServerCommand("rescan", arr) =>
Rescan.fromJsArr(arr) match {
case Failure(exception) =>

View file

@ -183,7 +183,13 @@ For more information on how to use our built in `cli` to interact with the serve
- `opreturncommit` `message` `[options]` - Creates OP_RETURN commitment transaction
- `message` - message to put into OP_RETURN commitment
- `--hashMessage` - should the message be hashed before commitment
- `--feerate <value>` - Fee rate in sats per virtual byte
- `--feerate <value>` - Fee rate in sats per virtual byte
- `bumpfeecpfp` `txid` `feerate` - Bump the fee of the given transaction id with a child tx using the given fee rate
- `txid` - Id of transaction to bump fee
- `feerate` - Fee rate in sats per virtual byte of the child transaction
- `bumpfeerbf` `txid` `feerate` - Replace given transaction with one with the new fee rate
- `txid` - Id of transaction to bump fee
- `feerate` - New fee rate in sats per virtual byte
- `gettransaction` `txid` - Get detailed information about in-wallet transaction <txid>
- `txid` - The transaction id
- `lockunspent` `unlock` `transactions` - Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.