mirror of
https://github.com/ACINQ/eclair.git
synced 2025-02-23 22:46:44 +01:00
Support multiple hops in RoutingInfoTag (#185)
* Support multiple hops in RoutingInfoTag * Change `HiddenHop` to `ExtraHop`, `channelId: BinaryData` to `shortChannelId: Long`
This commit is contained in:
parent
f13e07850b
commit
5becef6fc6
2 changed files with 42 additions and 16 deletions
|
@ -200,20 +200,45 @@ object PaymentRequest {
|
|||
}
|
||||
|
||||
/**
|
||||
* Routing Info Tag
|
||||
* Hidden hop
|
||||
*
|
||||
* @param pubkey node id
|
||||
* @param channelId channel id
|
||||
* @param shortChannelId channel id
|
||||
* @param fee node fee
|
||||
* @param cltvExpiryDelta node cltv expiry delta
|
||||
*/
|
||||
case class RoutingInfoTag(pubkey: PublicKey, channelId: BinaryData, fee: Long, cltvExpiryDelta: Int) extends Tag {
|
||||
case class ExtraHop(pubkey: PublicKey, shortChannelId: Long, fee: Long, cltvExpiryDelta: Int) {
|
||||
def pack: Seq[Byte] = pubkey.toBin ++ Protocol.writeUInt64(shortChannelId, ByteOrder.BIG_ENDIAN) ++
|
||||
Protocol.writeUInt64(fee, ByteOrder.BIG_ENDIAN) ++ Protocol.writeUInt16(cltvExpiryDelta, ByteOrder.BIG_ENDIAN)
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing Info Tag
|
||||
*
|
||||
* @param path one or more entries containing extra routing information for a private route
|
||||
*/
|
||||
case class RoutingInfoTag(path: Seq[ExtraHop]) extends Tag {
|
||||
override def toInt5s = {
|
||||
val ints = Bech32.eight2five(pubkey.toBin ++ channelId ++ Protocol.writeUInt64(fee, ByteOrder.BIG_ENDIAN) ++ Protocol.writeUInt16(cltvExpiryDelta, ByteOrder.BIG_ENDIAN))
|
||||
val ints = Bech32.eight2five(path.flatMap(_.pack))
|
||||
Seq(Bech32.map('r'), (ints.length / 32).toByte, (ints.length % 32).toByte) ++ ints
|
||||
}
|
||||
}
|
||||
|
||||
object RoutingInfoTag {
|
||||
def parse(data: Seq[Byte]) = {
|
||||
val pubkey = data.slice(0, 33)
|
||||
val shortChannelId = Protocol.uint64(data.slice(33, 33 + 8), ByteOrder.BIG_ENDIAN)
|
||||
val fee = Protocol.uint64(data.slice(33 + 8, 33 + 8 + 8), ByteOrder.BIG_ENDIAN)
|
||||
val cltv = Protocol.uint16(data.slice(33 + 8 + 8, chunkLength), ByteOrder.BIG_ENDIAN)
|
||||
ExtraHop(PublicKey(pubkey), shortChannelId, fee, cltv)
|
||||
}
|
||||
|
||||
def parseAll(data: Seq[Byte]): Seq[ExtraHop] =
|
||||
data.grouped(chunkLength).map(parse).toList
|
||||
|
||||
val chunkLength: Int = 33 + 8 + 8 + 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Expiry Date
|
||||
*
|
||||
|
@ -284,11 +309,8 @@ object PaymentRequest {
|
|||
}
|
||||
case r if r == Bech32.map('r') =>
|
||||
val data = Bech32.five2eight(input.drop(3).take(len))
|
||||
val pubkey = PublicKey(data.take(33))
|
||||
val channelId = data.drop(33).take(8)
|
||||
val fee = Protocol.uint64(data.drop(33 + 8), ByteOrder.BIG_ENDIAN)
|
||||
val cltv = Protocol.uint16(data.drop(33 + 8 + 8), ByteOrder.BIG_ENDIAN)
|
||||
RoutingInfoTag(pubkey, channelId, fee, cltv)
|
||||
val path = RoutingInfoTag.parseAll(data)
|
||||
RoutingInfoTag(path)
|
||||
case x if x == Bech32.map('x') =>
|
||||
require(len == 2, s"invalid length for expiry tag, should be 2 instead of $len")
|
||||
val expiry = 32 * input(3) + input(4)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package fr.acinq.eclair.payment
|
||||
|
||||
import java.nio.ByteOrder
|
||||
|
||||
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
|
||||
import fr.acinq.bitcoin.{BinaryData, Btc, Crypto, MilliBtc, MilliSatoshi, Satoshi}
|
||||
import fr.acinq.eclair.payment.PaymentRequest.{Amount, RoutingInfoTag}
|
||||
import fr.acinq.bitcoin.{BinaryData, Btc, Crypto, MilliBtc, MilliSatoshi, Protocol, Satoshi}
|
||||
import fr.acinq.eclair.payment.PaymentRequest.{Amount, ExtraHop, RoutingInfoTag}
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.FunSuite
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
|
@ -94,20 +96,22 @@ class PaymentRequestSpec extends FunSuite {
|
|||
assert(PaymentRequest.write(pr.sign(priv)) == ref)
|
||||
}
|
||||
|
||||
test("On mainnet, with fallback address 1RustyRX2oai4EYYDpQGWvEL62BBGqN9T with extra routing info to get to node 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255") {
|
||||
val ref = "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85frzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvncsk57n4v9ehw86wq8fzvjejhv9z3w3q5zh6qkql005x9xl240ch23jk79ujzvr4hsmmafyxghpqe79psktnjl668ntaf4ne7ucs5csqh5mnnk"
|
||||
test("On mainnet, with fallback address 1RustyRX2oai4EYYDpQGWvEL62BBGqN9T with extra routing info to go via nodes 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255 then 039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255") {
|
||||
val ref = "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqqqqqqq7qqzqfnlkwydm8rg30gjku7wmxmk06sevjp53fmvrcfegvwy7d5443jvyhxsel0hulkstws7vqv400q4j3wgpk4crg49682hr4scqvmad43cqd5m7tf"
|
||||
val pr = PaymentRequest.read(ref)
|
||||
assert(pr.prefix == "lnbc")
|
||||
assert(pr.amount == Some(MilliSatoshi(2000000000L)))
|
||||
assert(pr.amount === Some(MilliSatoshi(2000000000L)))
|
||||
assert(pr.paymentHash == BinaryData("0001020304050607080900010203040506070809000102030405060708090102"))
|
||||
assert(pr.timestamp == 1496314658L)
|
||||
assert(pr.nodeId == PublicKey(BinaryData("03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad")))
|
||||
assert(pr.description == Right(Crypto.sha256("One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon".getBytes)))
|
||||
assert(pr.fallbackAddress === Some("1RustyRX2oai4EYYDpQGWvEL62BBGqN9T"))
|
||||
assert(pr.routingInfo() === RoutingInfoTag(PublicKey("029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255"), "0102030405060708", 20, 3) :: Nil)
|
||||
assert(pr.routingInfo() === List(RoutingInfoTag(List(ExtraHop(PublicKey("029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255"), 72623859790382856L, 20, 3), ExtraHop(PublicKey("039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255"), 217304205466536202L, 30, 4)))))
|
||||
assert(BinaryData(Protocol.writeUInt64(72623859790382856L, ByteOrder.BIG_ENDIAN)) == BinaryData("0102030405060708"))
|
||||
assert(BinaryData(Protocol.writeUInt64(217304205466536202L, ByteOrder.BIG_ENDIAN)) == BinaryData("030405060708090a"))
|
||||
assert(pr.tags.size == 4)
|
||||
assert(PaymentRequest.write(pr.sign(priv)) == ref)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test("On mainnet, with fallback (p2sh) address 3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX") {
|
||||
|
|
Loading…
Add table
Reference in a new issue