1
0
mirror of https://github.com/ACINQ/eclair.git synced 2024-11-20 02:27:32 +01:00

Added a channel version to Commitments object (#1059)

In a backward-compatible way, by using the fact that the first object of
a legacy `Commitments` was a public key, starting from 0x02 or 0x03.
This commit is contained in:
Pierre-Marie Padiou 2019-07-12 13:36:33 +02:00 committed by GitHub
parent 1621e393dd
commit c1a7b4fe50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 46 additions and 13 deletions

View File

@ -24,7 +24,7 @@ import de.heikoseeberger.akkahttpjson4s.Json4sSupport
import de.heikoseeberger.akkahttpjson4s.Json4sSupport.ShouldWritePretty
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, MilliSatoshi, OutPoint, Transaction}
import fr.acinq.eclair.channel.State
import fr.acinq.eclair.channel.{ChannelVersion, State}
import fr.acinq.eclair.crypto.ShaChain
import fr.acinq.eclair.db.OutgoingPaymentStatus
import fr.acinq.eclair.payment.PaymentRequest
@ -81,6 +81,9 @@ class PrivateKeySerializer extends CustomSerializer[PrivateKey](format => ({ nul
case x: PrivateKey => JString("XXX")
}))
class ChannelVersionSerializer extends CustomSerializer[ChannelVersion](format => ({ null }, {
case x: ChannelVersion => JString(x.toString)
}))
class TransactionSerializer extends CustomSerializer[TransactionWithInputInfo](ser = format => ({ null }, {
case x: Transaction => JObject(List(
@ -195,6 +198,7 @@ object JsonSupport extends Json4sSupport {
new InetSocketAddressSerializer +
new OutPointSerializer +
new OutPointKeySerializer +
new ChannelVersionSerializer +
new InputInfoSerializer +
new ColorSerializer +
new RouteResponseSerializer +

View File

@ -419,7 +419,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
channelId = channelId,
signature = localSigOfRemoteTx
)
val commitments = Commitments(localParams, remoteParams, channelFlags,
val commitments = Commitments(ChannelVersion.STANDARD, localParams, remoteParams, channelFlags,
LocalCommit(0, localSpec, PublishableTxs(signedLocalCommitTx, Nil)), RemoteCommit(0, remoteSpec, remoteCommitTx.tx.txid, remoteFirstPerCommitmentPoint),
LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil, Nil),
localNextHtlcId = 0L, remoteNextHtlcId = 0L,
@ -457,7 +457,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
handleLocalError(InvalidCommitmentSignature(channelId, signedLocalCommitTx.tx), d, Some(msg))
case Success(_) =>
val commitInput = localCommitTx.input
val commitments = Commitments(localParams, remoteParams, channelFlags,
val commitments = Commitments(ChannelVersion.STANDARD, localParams, remoteParams, channelFlags,
LocalCommit(0, localSpec, PublishableTxs(signedLocalCommitTx, Nil)), remoteCommit,
LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil, Nil),
localNextHtlcId = 0L, remoteNextHtlcId = 0L,

View File

@ -222,4 +222,9 @@ object ChannelFlags {
val AnnounceChannel = 0x01.toByte
val Empty = 0x00.toByte
}
sealed trait ChannelVersion
object ChannelVersion {
case object STANDARD extends ChannelVersion
}
// @formatter:on

View File

@ -17,7 +17,7 @@
package fr.acinq.eclair.channel
import akka.event.LoggingAdapter
import fr.acinq.bitcoin.Crypto.{PublicKey, PrivateKey, sha256}
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey, sha256}
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, Satoshi}
import fr.acinq.eclair.crypto.{Generators, KeyManager, ShaChain, Sphinx}
import fr.acinq.eclair.payment._
@ -25,7 +25,6 @@ import fr.acinq.eclair.transactions.Transactions._
import fr.acinq.eclair.transactions._
import fr.acinq.eclair.wire._
import fr.acinq.eclair.{Globals, UInt64}
import scodec.bits.ByteVector
import scala.util.{Failure, Success}
@ -50,7 +49,8 @@ case class WaitingForRevocation(nextRemoteCommit: RemoteCommit, sent: CommitSig,
* So, when we've signed and sent a commit message and are waiting for their revocation message,
* theirNextCommitInfo is their next commit tx. The rest of the time, it is their next per-commitment point
*/
case class Commitments(localParams: LocalParams, remoteParams: RemoteParams,
case class Commitments(channelVersion: ChannelVersion,
localParams: LocalParams, remoteParams: RemoteParams,
channelFlags: Byte,
localCommit: LocalCommit, remoteCommit: RemoteCommit,
localChanges: LocalChanges, remoteChanges: RemoteChanges,
@ -475,7 +475,7 @@ object Commitments {
case Left(_) if revocation.perCommitmentSecret.publicKey != remoteCommit.remotePerCommitmentPoint =>
throw InvalidRevocation(commitments.channelId)
case Left(WaitingForRevocation(theirNextCommit, _, _, _)) =>
val forwards = commitments.remoteChanges.signed collect {
val forwards = commitments.remoteChanges.signed collect {
// we forward adds downstream only when they have been committed by both sides
// it always happen when we receive a revocation, because they send the add, then they sign it, then we sign it
case add: UpdateAddHtlc => ForwardAdd(add)

View File

@ -50,6 +50,14 @@ object ChannelCodecs extends Logging {
("path" | keyPathCodec) ::
("parent" | int64)).as[ExtendedPrivateKey]
val channelVersionCodec: Codec[ChannelVersion] = discriminatorWithDefault[ChannelVersion](
discriminator = discriminated[ChannelVersion].by(byte)
.typecase(0x01, provide(ChannelVersion.STANDARD))
// NB: 0x02 and 0x03 are *reserved* for backward compatibility reasons
,
fallback = provide(ChannelVersion.STANDARD)
)
val localParamsCodec: Codec[LocalParams] = (
("nodeId" | publicKey) ::
("channelPath" | keyPathCodec) ::
@ -204,7 +212,8 @@ object ChannelCodecs extends Logging {
)
val commitmentsCodec: Codec[Commitments] = (
("localParams" | localParamsCodec) ::
("channelVersion" | channelVersionCodec) ::
("localParams" | localParamsCodec) ::
("remoteParams" | remoteParamsCodec) ::
("channelFlags" | byte) ::
("localCommit" | localCommitCodec) ::

View File

@ -20,7 +20,7 @@ import java.util.UUID
import fr.acinq.bitcoin.DeterministicWallet.ExtendedPrivateKey
import fr.acinq.bitcoin.{Block, ByteVector32, Crypto, DeterministicWallet}
import fr.acinq.eclair.channel.{Channel, Commitments}
import fr.acinq.eclair.channel.{Channel, ChannelVersion, Commitments}
import fr.acinq.eclair.crypto.Sphinx
import fr.acinq.eclair.crypto.Sphinx.{PacketAndSecrets, ParsedPacket}
import fr.acinq.eclair.payment.PaymentLifecycle._
@ -152,7 +152,7 @@ class HtlcGenerationSpec extends FunSuite {
object HtlcGenerationSpec {
def makeCommitments(channelId: ByteVector32, availableBalanceForSend: Long = 50000000L, availableBalanceForReceive: Long = 50000000L) =
new Commitments(null, null, 0.toByte, null, null, null, null, 0, 0, Map.empty, null, null, null, channelId) {
new Commitments(ChannelVersion.STANDARD, null, null, 0.toByte, null, null, null, null, 0, 0, Map.empty, null, null, null, channelId) {
override lazy val availableBalanceForSendMsat: Long = availableBalanceForSend.max(0)
override lazy val availableBalanceForReceiveMsat: Long = availableBalanceForReceive.max(0)
}

View File

@ -64,6 +64,21 @@ class ChannelCodecsSpec extends FunSuite {
assert(keyPath === decoded.value)
}
test("encode/decode channel version in a backward compatible way") {
// before we had commitment version, public keys were stored first (they started with 0x02 and 0x03)
val legacy02 = hex"02a06ea3081f0f7a8ce31eb4f0822d10d2da120d5a1b1451f0727f51c7372f0f9b"
val legacy03 = hex"03d5c030835d6a6248b2d1d4cac60813838011b995a66b6f78dcc9fb8b5c40c3f3"
val current02 = hex"0102a06ea3081f0f7a8ce31eb4f0822d10d2da120d5a1b1451f0727f51c7372f0f9b"
val current03 = hex"0103d5c030835d6a6248b2d1d4cac60813838011b995a66b6f78dcc9fb8b5c40c3f3"
assert(channelVersionCodec.decode(legacy02.bits) === Attempt.successful(DecodeResult(ChannelVersion.STANDARD, legacy02.bits)))
assert(channelVersionCodec.decode(legacy03.bits) === Attempt.successful(DecodeResult(ChannelVersion.STANDARD, legacy03.bits)))
assert(channelVersionCodec.decode(current02.bits) === Attempt.successful(DecodeResult(ChannelVersion.STANDARD, current02.drop(1).bits)))
assert(channelVersionCodec.decode(current03.bits) === Attempt.successful(DecodeResult(ChannelVersion.STANDARD, current03.drop(1).bits)))
assert(channelVersionCodec.encode(ChannelVersion.STANDARD) === Attempt.successful(hex"01".bits))
}
test("encode/decode localparams") {
val o = LocalParams(
nodeId = randomKey.publicKey,
@ -297,8 +312,8 @@ class ChannelCodecsSpec extends FunSuite {
// and we decode with the new codec
val newnormal = stateDataCodec.decode(newbin.bits).require.value
// finally we check that the actual data is the same as before (we just remove the new json field)
val oldjson = Serialization.write(oldnormal)(JsonSupport.formats).replace(""","unknownFields":""""", "")
val newjson = Serialization.write(newnormal)(JsonSupport.formats).replace(""","unknownFields":""""", "")
val oldjson = Serialization.write(oldnormal)(JsonSupport.formats).replace(""","unknownFields":""""", "").replace(""""channelVersion":"STANDARD",""", "")
val newjson = Serialization.write(newnormal)(JsonSupport.formats).replace(""","unknownFields":""""", "").replace(""""channelVersion":"STANDARD",""", "")
assert(oldjson === refjson)
assert(newjson === refjson)
}
@ -361,7 +376,7 @@ object ChannelCodecsSpec {
val localCommit = LocalCommit(0, CommitmentSpec(htlcs.toSet, 1500, 50000000, 70000000), PublishableTxs(CommitTx(commitmentInput, Transaction(2, Nil, Nil, 0)), Nil))
val remoteCommit = RemoteCommit(0, CommitmentSpec(htlcs.map(htlc => htlc.copy(direction = htlc.direction.opposite)).toSet, 1500, 50000, 700000), ByteVector32(hex"0303030303030303030303030303030303030303030303030303030303030303"), PrivateKey(ByteVector.fill(32)(4)).publicKey)
val commitments = Commitments(localParams, remoteParams, channelFlags = 0x01.toByte, localCommit, remoteCommit, LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil, Nil),
val commitments = Commitments(ChannelVersion.STANDARD, localParams, remoteParams, channelFlags = 0x01.toByte, localCommit, remoteCommit, LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil, Nil),
localNextHtlcId = 32L,
remoteNextHtlcId = 4L,
originChannels = Map(42L -> Local(UUID.randomUUID, None), 15000L -> Relayed(ByteVector32(ByteVector.fill(32)(42)), 43, 11000000L, 10000000L)),