Skip to content

Commit

Permalink
accepting resizes.
Browse files Browse the repository at this point in the history
  • Loading branch information
fiatjaf committed Sep 27, 2022
1 parent 900df28 commit a7c0ff5
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 17 deletions.
32 changes: 32 additions & 0 deletions src/main/scala/CLN.scala
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,11 @@ class CLN() extends NodeInterface {
"usage" -> "peerid msatoshi",
"description" -> "Proposes overriding the state of the channel with {peerid} with the next local balance being equal to {msatoshi}."
),
ujson.Obj(
"name" -> "hc-resize",
"usage" -> "peerid msatoshi",
"description" -> "Prepares the channel with {peerid} to resize its max capacity up to {msatoshi} (after calling this on the host the client must issue a resize command on its side). Calling it with {msatoshi} set to zero cancels the resize."
),
ujson.Obj(
"name" -> "hc-request-channel",
"usage" -> "peerid",
Expand Down Expand Up @@ -697,6 +702,33 @@ class CLN() extends NodeInterface {
}
}

case "hc-resize" =>
(params match {
case _: ujson.Obj =>
Some((params("peerid").strOpt, params("msatoshi").numOpt))
case arr: ujson.Arr if arr.value.size == 2 =>
Some((params(0).strOpt, params(1).numOpt))
case _ => None
}) match {
case Some(Some(peerId), Some(satoshi)) => {
val upTo = satoshi.toLong match {
case 0 => None
case s => Some(MilliSatoshi(s).toSatoshi)
}

ChannelMaster
.getChannel(ByteVector.fromValidHex(peerId))
.acceptResize(upTo)
.onComplete {
case Success(msg) => reply(msg)
case Failure(err) => replyError(err.toString)
}
}
case _ => {
replyError("invalid parameters")
}
}

case "hc-request-channel" =>
(params match {
case _: ujson.Obj =>
Expand Down
100 changes: 84 additions & 16 deletions src/main/scala/Channel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,55 @@ class Channel(peerId: ByteVector) {
}
}

// a client wants their channel to be resized
case msg: ResizeChannel
if status == Active &&
msg.verifyClientSig(PublicKey(peerId)) &&
msg.newCapacity > state.lcssNext.remoteBalanceMsat.toSatoshi &&
currentData.acceptingResize
.map(msg.newCapacity <= _)
.getOrElse(false) =>
localLogger.info
.item("sats", msg.newCapacity)
.msg("accepting resize proposal")

def resizedInit(init: InitHostedChannel): InitHostedChannel = init.copy(
channelCapacityMsat = msg.newCapacity.toMilliSatoshi,
maxHtlcValueInFlightMsat =
UInt64(msg.newCapacity.toMilliSatoshi.toLong),
)
def resizedBalance(curr: MilliSatoshi): MilliSatoshi =
curr + msg.newCapacity - lcssStored.initHostedChannel.channelCapacityMsat

// change our next state
state = state.copy(lcssCurrent =
state.lcssCurrent.copy(
initHostedChannel =
resizedInit(state.lcssCurrent.initHostedChannel),
localBalanceMsat =
resizedBalance(state.lcssCurrent.localBalanceMsat)
)
)

// change in the database
ChannelMaster.database.update { data =>
data
// change accepting resize to None again
.modify(_.channels.at(peerId).acceptingResize)
.setTo(None)

// increase our balance accordingly (or decrease)
.modify(_.channels.at(peerId).lcss.localBalanceMsat)
.using(resizedBalance(_))

// adjust init params
.modify(_.channels.at(peerId).lcss.initHostedChannel)
.using(resizedInit(_))
}

// send a state_update with the resized parameters
sendStateUpdate(state)

// a client is telling us they are online
case msg: InvokeHostedChannel if status == Active =>
// after a reconnection our peer won't have any of our
Expand Down Expand Up @@ -970,23 +1019,12 @@ class Channel(peerId: ByteVector) {
.msg("we and the client are now even")
// verify signature
if (!lcssNext.verifyRemoteSig(PublicKey(peerId))) {
// a wrong signature, fail the channel
val err = Error(
channelId,
HostedError.ERR_HOSTED_WRONG_REMOTE_SIG
// a wrong signature, technically we should fail the channel
// here, but couldn't this just be a mismatch between states?
// let's be friendly and just ignore this message
localLogger.debug.msg(
"state update from peer has an invalid signature, ignoring it"
)
sendMessage(err)
ChannelMaster.database.update { data =>
data
.modify(_.channels.at(peerId).localErrors)
.using(
_ + DetailedError(
err,
None,
"peer sent a wrong state update or one with a broken signature"
)
)
}
} else {
// grab state before saving the update
val lcssPrev = lcssStored
Expand Down Expand Up @@ -1412,6 +1450,36 @@ class Channel(peerId: ByteVector) {
}
}

def acceptResize(upTo: Option[Satoshi]): Future[String] = {
logger.debug
.item(status)
.item("up-to-satoshis", upTo)
.msg("accepting resize")

if (status != Active) {
Future.failed(
new Exception(
"can't resize channel since it is not in a healthy state."
)
)
} else if (!currentData.lcss.isHost) {
Future.failed(
new Exception(
"can't resize this channel since we are not the hosts."
)
)
} else
Future {
ChannelMaster.database.update { data =>
data
.modify(_.channels.at(peerId).acceptingResize)
.setTo(upTo)
}

s"resize to $upTo prepared"
}
}

def getChannelUpdate(channelIsUp: Boolean): ChannelUpdate = {
val flags = ChannelUpdate.ChannelFlags(
isNode1 = LexicographicalOrdering
Expand Down
3 changes: 2 additions & 1 deletion src/main/scala/Database.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ case class ChannelData(
lcss: LastCrossSignedState = HostedChannelHelpers.lcssEmpty,
localErrors: Set[DetailedError] = Set.empty,
remoteErrors: Set[Error] = Set.empty,
suspended: Boolean = false,
proposedOverride: Option[LastCrossSignedState] = None,
suspended: Boolean = false
acceptingResize: Option[Satoshi] = None
)

case class DetailedError(
Expand Down
2 changes: 2 additions & 0 deletions src/main/scala/Picklers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ object Picklers {
.bimap[ByteVector64](_.toHex, ByteVector64.fromValidHex(_))
given ReadWriter[MilliSatoshi] =
readwriter[Long].bimap[MilliSatoshi](_.toLong, MilliSatoshi(_))
given ReadWriter[Satoshi] =
readwriter[Long].bimap[Satoshi](_.toLong, Satoshi(_))
given ReadWriter[ShortChannelId] =
readwriter[String].bimap[ShortChannelId](_.toString, ShortChannelId(_))
given ReadWriter[CltvExpiry] =
Expand Down
4 changes: 4 additions & 0 deletions src/main/scala/Printer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,15 @@ object Printer {
"local" -> data.lcss.localUpdates.toInt,
"remote" -> data.lcss.remoteUpdates.toInt
),
"acceptingResize" -> data.acceptingResize.map(_.toLong.toInt),
"errors" -> ujson.Obj(
"local" -> data.localErrors
.map(dtlerr => ujson.Str(dtlerr.toString)),
"remote" -> data.remoteErrors
.map(err => ujson.Str(err.toString))
),
"proposedOverride" -> data.proposedOverride.map(
_.localBalanceMsat.toLong.toInt
)
)

Expand Down

0 comments on commit a7c0ff5

Please sign in to comment.