Skip to content

Commit

Permalink
Introduce scodecs for new LocalParamsWithDKP
Browse files Browse the repository at this point in the history
  • Loading branch information
araspitzu committed Jul 22, 2019
1 parent b5c5cda commit 8d58abc
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,22 @@ final case class LocalParams(nodeId: PublicKey,
globalFeatures: ByteVector,
localFeatures: ByteVector)

final case class LocalParamsWithDKP(nodeId: PublicKey,
channelKeyPath: Either[DeterministicWallet.KeyPath, KeyPathFundee],
dustLimitSatoshis: Long,
maxHtlcValueInFlightMsat: UInt64,
channelReserveSatoshis: Long,
htlcMinimumMsat: Long,
toSelfDelay: Int,
maxAcceptedHtlcs: Int,
defaultFinalScriptPubKey: ByteVector,
globalFeatures: ByteVector,
localFeatures: ByteVector) {
def isFunder = channelKeyPath.isLeft
}

case class KeyPathFundee(fundingKeyPath: KeyPath, pointsKeyPath: KeyPath)

final case class RemoteParams(nodeId: PublicKey,
dustLimitSatoshis: Long,
maxHtlcValueInFlightMsat: UInt64,
Expand All @@ -235,5 +251,6 @@ case class ChannelVersion(bits: BitVector) {
object ChannelVersion {
val LENGTH_BITS = 4 * 8
val STANDARD = ChannelVersion(BitVector.fill(LENGTH_BITS)(false))
val DETERMINISTIC_KEYPATH = ChannelVersion(BitVector.one ++ BitVector.fill(LENGTH_BITS -1)(false))
}
// @formatter:on
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ package fr.acinq.eclair.wire
import java.util.UUID

import akka.actor.ActorRef
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.bitcoin.DeterministicWallet.{ExtendedPrivateKey, KeyPath}
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, OutPoint, Transaction, TxOut}
import fr.acinq.eclair.UInt64
import fr.acinq.eclair.channel.ChannelVersion.{DETERMINISTIC_KEYPATH, STANDARD}
import fr.acinq.eclair.channel._
import fr.acinq.eclair.crypto.ShaChain
import fr.acinq.eclair.payment.{Local, Origin, Relayed}
Expand All @@ -29,9 +32,10 @@ import fr.acinq.eclair.transactions._
import fr.acinq.eclair.wire.CommonCodecs._
import fr.acinq.eclair.wire.LightningMessageCodecs._
import grizzled.slf4j.Logging
import scodec.bits.BitVector
import scodec.bits.{BitVector, ByteVector}
import scodec.codecs._
import scodec.{Attempt, Codec}
import scodec.{Attempt, Codec, Decoder, Encoder}
import shapeless.{HList, HNil}

import scala.compat.Platform
import scala.concurrent.duration._
Expand All @@ -42,6 +46,12 @@ import scala.concurrent.duration._
object ChannelCodecs extends Logging {

val keyPathCodec: Codec[KeyPath] = ("path" | listOfN(uint16, uint32)).xmap[KeyPath](l => new KeyPath(l), keyPath => keyPath.path.toList).as[KeyPath]
val keyPathFundeeCodec: Codec[KeyPathFundee] = (
("fundingKeyPath" | keyPathCodec) ::
("pointsKeyPath" | keyPathCodec)
).as[KeyPathFundee]

val channelKeyPathCodec = either(bool, keyPathCodec, keyPathFundeeCodec)

val extendedPrivateKeyCodec: Codec[ExtendedPrivateKey] = (
("secretkeybytes" | bytes32) ::
Expand Down Expand Up @@ -72,6 +82,80 @@ object ChannelCodecs extends Logging {
("globalFeatures" | varsizebinarydata) ::
("localFeatures" | varsizebinarydata)).as[LocalParams]


import shapeless._
val localParamsCodecPreDKP: Codec[LocalParamsWithDKP] = (
("nodeId" | publicKey) ::
("channelPath" | keyPathCodec) ::
("dustLimitSatoshis" | uint64overflow) ::
("maxHtlcValueInFlightMsat" | uint64) ::
("channelReserveSatoshis" | uint64overflow) ::
("htlcMinimumMsat" | uint64overflow) ::
("toSelfDelay" | uint16) ::
("maxAcceptedHtlcs" | uint16) ::
("isFunder" | bool) ::
("defaultFinalScriptPubKey" | varsizebinarydata) ::
("globalFeatures" | varsizebinarydata) ::
("localFeatures" | varsizebinarydata)).xmap({
case nodeId ::
keyPath ::
dustLimitSatoshis ::
maxHtlcValueInFlightMsat ::
channelReserveSatoshis ::
htlcMinimumMsat ::
toSelfDelay ::
maxAcceptedHtlcs ::
isFunder ::
defaultFinalScriptPubKey ::
globalFeatures :: localFeatures :: HNil =>

LocalParamsWithDKP(
nodeId = nodeId,
// old versions of "LocalParams" use a single keypath
channelKeyPath = if(isFunder) Left(keyPath) else Right(KeyPathFundee(keyPath, keyPath)),
dustLimitSatoshis = dustLimitSatoshis,
maxHtlcValueInFlightMsat = maxHtlcValueInFlightMsat,
channelReserveSatoshis = channelReserveSatoshis,
htlcMinimumMsat = htlcMinimumMsat,
toSelfDelay = toSelfDelay,
maxAcceptedHtlcs = maxAcceptedHtlcs,
defaultFinalScriptPubKey = defaultFinalScriptPubKey,
globalFeatures = globalFeatures,
localFeatures = localFeatures)
},{ localParams =>

localParams.nodeId ::
localParams.channelKeyPath.fold(kp => kp, fKp => fKp.fundingKeyPath) :: // when encoding an old version of local params 'KeyPathFundee' holds 2 identical keypaths
localParams.dustLimitSatoshis ::
localParams.maxHtlcValueInFlightMsat ::
localParams.channelReserveSatoshis ::
localParams.htlcMinimumMsat ::
localParams.toSelfDelay ::
localParams.maxAcceptedHtlcs ::
localParams.isFunder ::
localParams.defaultFinalScriptPubKey ::
localParams.globalFeatures :: localParams.localFeatures :: HNil
})

val localParamsCodecCurrent: Codec[LocalParamsWithDKP] = (
("nodeId" | publicKey) ::
("channelPath" | channelKeyPathCodec) ::
("dustLimitSatoshis" | uint64overflow) ::
("maxHtlcValueInFlightMsat" | uint64) ::
("channelReserveSatoshis" | uint64overflow) ::
("htlcMinimumMsat" | uint64overflow) ::
("toSelfDelay" | uint16) ::
("maxAcceptedHtlcs" | uint16) ::
("defaultFinalScriptPubKey" | varsizebinarydata) ::
("globalFeatures" | varsizebinarydata) ::
("localFeatures" | varsizebinarydata)).as[LocalParamsWithDKP]

// unused for now
def localParamsCodecWithVersion(version: ChannelVersion): Codec[LocalParamsWithDKP] = version match {
case STANDARD => localParamsCodecPreDKP
case DETERMINISTIC_KEYPATH => localParamsCodecCurrent
}

val remoteParamsCodec: Codec[RemoteParams] = (
("nodeId" | publicKey) ::
("dustLimitSatoshis" | uint64overflow) ::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import java.util.UUID
import akka.actor.ActorSystem
import fr.acinq.bitcoin.Crypto.PrivateKey
import fr.acinq.bitcoin.DeterministicWallet.KeyPath
import fr.acinq.bitcoin.{Crypto, DeterministicWallet, OutPoint}
import fr.acinq.bitcoin.{Block, ByteVector32, Crypto, DeterministicWallet, MilliSatoshi, OutPoint, Satoshi, Transaction}
import fr.acinq.eclair._
import fr.acinq.eclair.api.JsonSupport
import fr.acinq.eclair.channel.ChannelVersion.STANDARD
import fr.acinq.eclair.channel.Helpers.Funding
import fr.acinq.eclair.channel._
import fr.acinq.eclair.crypto.{LocalKeyManager, ShaChain, Sphinx}
Expand Down Expand Up @@ -80,22 +82,39 @@ class ChannelCodecsSpec extends FunSuite {
}

test("encode/decode localparams") {
val o = LocalParams(
val localParamFundee = LocalParamsWithDKP(
nodeId = randomKey.publicKey,
channelKeyPath = DeterministicWallet.KeyPath(Seq(42L)),
channelKeyPath = Right(KeyPathFundee(KeyPath(Seq(4, 3, 2, 1L)), KeyPath(Seq(1, 2, 3, 4L)))),
dustLimitSatoshis = Random.nextInt(Int.MaxValue),
maxHtlcValueInFlightMsat = UInt64(Random.nextInt(Int.MaxValue)),
channelReserveSatoshis = Random.nextInt(Int.MaxValue),
htlcMinimumMsat = Random.nextInt(Int.MaxValue),
toSelfDelay = Random.nextInt(Short.MaxValue),
maxAcceptedHtlcs = Random.nextInt(Short.MaxValue),
defaultFinalScriptPubKey = randomBytes(10 + Random.nextInt(200)),
isFunder = Random.nextBoolean(),
globalFeatures = randomBytes(256),
localFeatures = randomBytes(256))
val encoded = localParamsCodec.encode(o).require
val decoded = localParamsCodec.decode(encoded).require
assert(o === decoded.value)

val encoded = localParamsCodecWithVersion(ChannelVersion.DETERMINISTIC_KEYPATH).encode(localParamFundee).require
val decoded = localParamsCodecWithVersion(ChannelVersion.DETERMINISTIC_KEYPATH).decode(encoded).require
assert(localParamFundee === decoded.value)

val localParamFunder = LocalParamsWithDKP(
nodeId = randomKey.publicKey,
channelKeyPath = Left(KeyPath(Seq(4, 3, 2, 1L))),
dustLimitSatoshis = Random.nextInt(Int.MaxValue),
maxHtlcValueInFlightMsat = UInt64(Random.nextInt(Int.MaxValue)),
channelReserveSatoshis = Random.nextInt(Int.MaxValue),
htlcMinimumMsat = Random.nextInt(Int.MaxValue),
toSelfDelay = Random.nextInt(Short.MaxValue),
maxAcceptedHtlcs = Random.nextInt(Short.MaxValue),
defaultFinalScriptPubKey = randomBytes(10 + Random.nextInt(200)),
globalFeatures = randomBytes(256),
localFeatures = randomBytes(256))

val encoded1 = localParamsCodecWithVersion(ChannelVersion.DETERMINISTIC_KEYPATH).encode(localParamFunder).require
val decoded1 = localParamsCodecWithVersion(ChannelVersion.DETERMINISTIC_KEYPATH).decode(encoded1).require
assert(localParamFunder === decoded1.value)
}

test("encode/decode remoteparams") {
Expand Down Expand Up @@ -200,6 +219,7 @@ class ChannelCodecsSpec extends FunSuite {
test("basic serialization test (NORMAL)") {
val data = normal
val bin = ChannelCodecs.DATA_NORMAL_Codec.encode(data).require
assert(data.commitments.channelVersion == STANDARD)
val check = ChannelCodecs.DATA_NORMAL_Codec.decodeValue(bin).require
assert(data.commitments.localCommit.spec === check.commitments.localCommit.spec)
assert(data === check)
Expand Down Expand Up @@ -311,11 +331,11 @@ class ChannelCodecsSpec extends FunSuite {
val newbin = stateDataCodec.encode(oldnormal).require.bytes
// 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":""""", "").replace(""""channelVersion":"00000000000000000000000000000000",""", "")
val newjson = Serialization.write(newnormal)(JsonSupport.formats).replace(""","unknownFields":""""", "").replace(""""channelVersion":"00000000000000000000000000000000",""", "")
assert(oldjson === refjson)
assert(newjson === refjson)
// finally we check that the actual data is the same as before
val oldjson = Serialization.write(oldnormal)(JsonSupport.formats)
val newjson = Serialization.write(newnormal)(JsonSupport.formats)

assert(oldjson === newjson)
}

}
Expand Down

0 comments on commit 8d58abc

Please sign in to comment.