Skip to content

Commit

Permalink
Update test vector for onion messages (#2876)
Browse files Browse the repository at this point in the history
  • Loading branch information
thomash-acinq committed Jul 10, 2024
1 parent 791edf7 commit 9762af8
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 43 deletions.
4 changes: 2 additions & 2 deletions eclair-core/src/test/resources/bolt4-test-onion-message.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@
"privkey": "4444444444444444444444444444444444444444444444444444444444444444",
"onion_message": "0201025aaca62db7ce6b46386206ef9930daa32e979a35cb185a41cb951aa7d254b03c055600025550b2910294fa73bda99b9de9c851be9cbb481e23194a1743033630efba546b86e7d838d0f6e9cc0ed088dbf6889f0dceca3bfc745bd77d013a31311fa932a8bf1d28387d9ff521eabc651dee8f861fed609a68551145a451f017ec44978addeee97a423c08445531da488fd1ddc998e9cdbfcea59517b53fbf1833f0bbe6188dba6ca773a247220ec934010daca9cc185e1ceb136803469baac799e27a0d82abe53dc48a06a55d1f643885cc7894677dd20a4e4152577d1ba74b870b9279f065f9b340cedb3ca13b7df218e853e10ccd1b59c42a2acf93f489e170ee4373d30ab158b60fc20d3ba73a1f8c750951d69fb5b9321b968ddc8114936412346aff802df65516e1c09c51ef19849ff36c0199fd88c8bec301a30fef0c7cb497901c038611303f64e4174b5daf42832aa5586b84d2c9b95f382f4269a5d1bd4be898618dc78dfd451170f72ca16decac5b03e60702112e439cadd104fb3bbb3d5023c9b80823fdcd0a212a7e1aaa6eeb027adc7f8b3723031d135a09a979a4802788bb7861c6cc85501fb91137768b70aeab309b27b885686604ffc387004ac4f8c44b101c39bc0597ef7fd957f53fc5051f534b10eb3852100962b5e58254e5558689913c26ad6072ea41f5c5db10077cfc91101d4ae393be274c74297da5cc381cd88d54753aaa7df74b2f9da8d88a72bc9218fcd1f19e4ff4aace182312b9509c5175b6988f044c5756d232af02a451a02ca752f3c52747773acff6fd07d2032e6ce562a2c42105d106eba02d0b1904182cdc8c74875b082d4989d3a7e9f0e73de7c75d357f4af976c28c0b206c5e8123fc2391d078592d0d5ff686fd245c0a2de2e535b7cca99c0a37d432a8657393a9e3ca53eec1692159046ba52cb9bc97107349d8673f74cbc97e231f1108005c8d03e24ca813cea2294b39a7a493bcc062708f1f6cf0074e387e7d50e0666ce784ef4d31cb860f6cad767438d9ea5156ff0ae86e029e0247bf94df75ee0cda4f2006061455cb2eaff513d558863ae334cef7a3d45f55e7cc13153c6719e9901c1d4db6c03f643b69ea4860690305651794284d9e61eb848ccdf5a77794d376f0af62e46d4835acce6fd9eef5df73ebb8ea3bb48629766967f446e744ecc57ff3642c4aa1ccee9a2f72d5caa75fa05787d08b79408fce792485fdecdc25df34820fb061275d70b84ece540b0fc47b2453612be34f2b78133a64e812598fbe225fd85415f8ffe5340ce955b5fd9d67dd88c1c531dde298ed25f96df271558c812c26fa386966c76f03a6ebccbca49ac955916929bd42e134f982dde03f924c464be5fd1ba44f8dc4c3cbc8162755fd1d8f7dc044b15b1a796c53df7d8769bb167b2045b49cc71e08908796c92c16a235717cabc4bb9f60f8f66ff4fff1f9836388a99583acebdff4a7fb20f48eedcd1f4bdcc06ec8b48e35307df51d9bc81d38a94992dd135b30079e1f592da6e98dff496cb1a7776460a26b06395b176f585636ebdf7eab692b227a31d6979f5a6141292698e91346b6c806b90c7c6971e481559cae92ee8f4136f2226861f5c39ddd29bbdb118a35dece03f49a96804caea79a3dacfbf09d65f2611b5622de51d98e18151acb3bb84c09caaa0cc80edfa743a4679f37d6167618ce99e73362fa6f213409931762618a61f1738c071bba5afc1db24fe94afb70c40d731908ab9a505f76f57a7d40e708fd3df0efc5b7cbb2a7b75cd23449e09684a2f0e2bfa0d6176c35f96fe94d92fc9fa4103972781f81cb6e8df7dbeb0fc529c600d768bed3f08828b773d284f69e9a203459d88c12d6df7a75be2455fec128f07a497a2b2bf626cc6272d0419ca663e9dc66b8224227eb796f0246dcae9c5b0b6cfdbbd40c3245a610481c92047c968c9fc92c04b89cc41a0c15355a8f",
"tlvs": {
"path_id": "deadbeefbadc0ffeedeadbeefbadc0ffeedeadbeefbadc0ffeedeadbeefbadc0",
"unknown_tag_1": "68656c6c6f"
"unknown_tag_1": "68656c6c6f",
"encrypted_recipient_data": "bdc03f088764c6224c8f939e321bf096f363b2092db381fc8787f891c8e6dc9284991b98d2a63d9f91fe563065366dd406cd8e112cdaaa80d0e6"
}
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,69 +261,87 @@ class OnionMessagesSpec extends AnyFunSuite {
assert(buildMessage(sessionKey, blindingSecret, IntermediateNode(alice.publicKey) :: IntermediateNode(bob.publicKey) :: Nil, Recipient(carol.publicKey, Some(pathId)), TlvStream.empty) == Left(MessageTooLarge(65433)))
}

private case class OnionMessageTestVector(comment: String, generate: OMTVGenerate, route: OMTVRoute, onionmessage: OMTVOnionmessage, decrypt: OMTVDecrypt)
private case class OMTVGenerate(comment: String, session_key: String, hops: Seq[OMTVGenerateHop])
private case class OMTVGenerateHop(alias: String, comment: String, blinding_secret: String, tlvs: OMTVTlvs, encrypted_data_tlv: String, encrypted_recipient_data: String, blinded_node_id: String)
private case class OMTVRoute(comment: String, introduction_node_id: String, blinding: String, hops: Seq[OMTVRouteHop])
private case class OMTVRouteHop(blinded_node_id: String, encrypted_recipient_data: String)
private case class OMTVOnionmessage(comment: String, onion_message_packet: String, unknown_tag_1: Option[String]) {
def getCustomTlvs: Set[GenericTlv] = Set(
unknown_tag_1.map(hex => GenericTlv(UInt64(1), ByteVector.fromValidHex(hex))),
).flatten
}
private case class OMTVDecrypt(comment: String, hops: Seq[OMTVDecryptHop])
private case class OMTVDecryptHop(alias: String, privkey: String, onion_message: String, next_node_id: Option[String], tlvs: Option[OMTVTlvs])
private case class OMTVTlvs(padding: Option[String],
path_id: Option[String],
next_node_id: Option[String],
next_blinding_override: Option[String],
encrypted_recipient_data: Option[String],
unknown_tag_1: Option[String],
unknown_tag_561: Option[String],
unknown_tag_65535: Option[String]) {
def getCustomTlvs: Set[GenericTlv] = Set(
unknown_tag_1.map(hex => GenericTlv(UInt64(1), ByteVector.fromValidHex(hex))),
unknown_tag_561.map(hex => GenericTlv(UInt64(561), ByteVector.fromValidHex(hex))),
unknown_tag_65535.map(hex => GenericTlv(UInt64(65535), ByteVector.fromValidHex(hex))),
).flatten
}

test("reference test vector") {
implicit val formats: Formats = DefaultFormats

val testVector = parse(getClass.getResourceAsStream("/bolt4-test-onion-message.json"))
val alice = PrivateKey(ByteVector32.fromValidHex(((testVector \ "decrypt" \ "hops")(0) \ "privkey").extract[String]))
val bob = PrivateKey(ByteVector32.fromValidHex(((testVector \ "decrypt" \ "hops")(1) \ "privkey").extract[String]))
val carol = PrivateKey(ByteVector32.fromValidHex(((testVector \ "decrypt" \ "hops")(2) \ "privkey").extract[String]))
val dave = PrivateKey(ByteVector32.fromValidHex(((testVector \ "decrypt" \ "hops")(3) \ "privkey").extract[String]))
assert(PublicKey(ByteVector.fromValidHex(((testVector \ "decrypt" \ "hops")(0) \ "next_node_id").extract[String])) == bob.publicKey)
assert(PublicKey(ByteVector.fromValidHex(((testVector \ "decrypt" \ "hops")(1) \ "next_node_id").extract[String])) == carol.publicKey)
assert(PublicKey(ByteVector.fromValidHex(((testVector \ "decrypt" \ "hops")(2) \ "next_node_id").extract[String])) == dave.publicKey)

def getCustomTlvs(json: JValue): Set[GenericTlv] = {
Set(
(json \ "unknown_tag_1").extract[Option[String]].map(hex => GenericTlv(UInt64(1), ByteVector.fromValidHex(hex))),
(json \ "unknown_tag_561").extract[Option[String]].map(hex => GenericTlv(UInt64(561), ByteVector.fromValidHex(hex))),
(json \ "unknown_tag_65535").extract[Option[String]].map(hex => GenericTlv(UInt64(65535), ByteVector.fromValidHex(hex))),
).flatten
}
val testVector = parse(getClass.getResourceAsStream("/bolt4-test-onion-message.json")).extract[OnionMessageTestVector]
val alice = PrivateKey(ByteVector32.fromValidHex(testVector.decrypt.hops(0).privkey))
val bob = PrivateKey(ByteVector32.fromValidHex(testVector.decrypt.hops(1).privkey))
val carol = PrivateKey(ByteVector32.fromValidHex(testVector.decrypt.hops(2).privkey))
val dave = PrivateKey(ByteVector32.fromValidHex(testVector.decrypt.hops(3).privkey))
assert(PublicKey(ByteVector.fromValidHex(testVector.decrypt.hops(0).next_node_id.get)) == bob.publicKey)
assert(PublicKey(ByteVector.fromValidHex(testVector.decrypt.hops(1).next_node_id.get)) == carol.publicKey)
assert(PublicKey(ByteVector.fromValidHex(testVector.decrypt.hops(2).next_node_id.get)) == dave.publicKey)

def makeRecipient(nodeKey: PrivateKey, json: JValue): Recipient =
Recipient(nodeKey.publicKey, Some(ByteVector.fromValidHex((json \ "path_id").extract[String])), (json \ "padding").extract[Option[String]].map(ByteVector.fromValidHex(_)), getCustomTlvs(json))
def makeRecipient(nodeKey: PrivateKey, tlvs: OMTVTlvs): Recipient =
Recipient(nodeKey.publicKey, Some(ByteVector.fromValidHex(tlvs.path_id.get)), tlvs.padding.map(ByteVector.fromValidHex(_)), tlvs.getCustomTlvs)

def makeIntermediateNode(nodeKey: PrivateKey, json: JValue): IntermediateNode =
IntermediateNode(nodeKey.publicKey, EncodedNodeId(nodeKey.publicKey), None, (json \ "padding").extract[Option[String]].map(ByteVector.fromValidHex(_)), getCustomTlvs(json))
def makeIntermediateNode(nodeKey: PrivateKey, tlvs: OMTVTlvs): IntermediateNode =
IntermediateNode(nodeKey.publicKey, EncodedNodeId(nodeKey.publicKey), None, tlvs.padding.map(ByteVector.fromValidHex(_)), tlvs.getCustomTlvs)

val blindingSecretBob = PrivateKey(ByteVector32.fromValidHex(((testVector \ "generate" \ "hops")(1) \ "blinding_secret").extract[String]))
val pathId = ByteVector.fromValidHex(((testVector \ "generate" \ "hops")(3) \ "tlvs" \ "path_id").extract[String])
val blindingSecretBob = PrivateKey(ByteVector32.fromValidHex(testVector.generate.hops(1).blinding_secret))
val pathId = ByteVector.fromValidHex(testVector.generate.hops(3).tlvs.path_id.get)
val pathBobToDave =
buildRoute(blindingSecretBob,
Seq(makeIntermediateNode(bob, (testVector \ "generate" \ "hops")(1) \ "tlvs"), makeIntermediateNode(carol, (testVector \ "generate" \ "hops")(2) \ "tlvs")),
makeRecipient(dave, (testVector \ "generate" \ "hops")(3) \ "tlvs")).route
val blindingSecretAlice = PrivateKey(ByteVector32.fromValidHex(((testVector \ "generate" \ "hops")(0) \ "blinding_secret").extract[String]))
val intermediateAlice = Seq(makeIntermediateNode(alice, (testVector \ "generate" \ "hops")(0) \ "tlvs"))
Seq(makeIntermediateNode(bob, testVector.generate.hops(1).tlvs), makeIntermediateNode(carol, testVector.generate.hops(2).tlvs)),
makeRecipient(dave, testVector.generate.hops(3).tlvs)).route
val blindingSecretAlice = PrivateKey(ByteVector32.fromValidHex(testVector.generate.hops(0).blinding_secret))
val intermediateAlice = Seq(makeIntermediateNode(alice, testVector.generate.hops(0).tlvs))
val pathAliceToDave = buildRouteFrom(blindingSecretAlice, intermediateAlice, BlindedPath(pathBobToDave))

val expectedPath = BlindedRoute(
EncodedNodeId(PublicKey(ByteVector.fromValidHex((testVector \ "route" \ "introduction_node_id").extract[String]))),
PublicKey(ByteVector.fromValidHex((testVector \ "route" \ "blinding").extract[String])),
EncodedNodeId(PublicKey(ByteVector.fromValidHex(testVector.route.introduction_node_id))),
PublicKey(ByteVector.fromValidHex(testVector.route.blinding)),
Seq(
BlindedNode(
PublicKey(ByteVector.fromValidHex(((testVector \ "route" \ "hops")(0) \ "blinded_node_id").extract[String])),
ByteVector.fromValidHex(((testVector \ "route" \ "hops")(0) \ "encrypted_recipient_data").extract[String])),
PublicKey(ByteVector.fromValidHex(testVector.route.hops(0).blinded_node_id)),
ByteVector.fromValidHex(testVector.route.hops(0).encrypted_recipient_data)),
BlindedNode(
PublicKey(ByteVector.fromValidHex(((testVector \ "route" \ "hops")(1) \ "blinded_node_id").extract[String])),
ByteVector.fromValidHex(((testVector \ "route" \ "hops")(1) \ "encrypted_recipient_data").extract[String])),
PublicKey(ByteVector.fromValidHex(testVector.route.hops(1).blinded_node_id)),
ByteVector.fromValidHex(testVector.route.hops(1).encrypted_recipient_data)),
BlindedNode(
PublicKey(ByteVector.fromValidHex(((testVector \ "route" \ "hops")(2) \ "blinded_node_id").extract[String])),
ByteVector.fromValidHex(((testVector \ "route" \ "hops")(2) \ "encrypted_recipient_data").extract[String])),
PublicKey(ByteVector.fromValidHex(testVector.route.hops(2).blinded_node_id)),
ByteVector.fromValidHex(testVector.route.hops(2).encrypted_recipient_data)),
BlindedNode(
PublicKey(ByteVector.fromValidHex(((testVector \ "route" \ "hops")(3) \ "blinded_node_id").extract[String])),
ByteVector.fromValidHex(((testVector \ "route" \ "hops")(3) \ "encrypted_recipient_data").extract[String])),
PublicKey(ByteVector.fromValidHex(testVector.route.hops(3).blinded_node_id)),
ByteVector.fromValidHex(testVector.route.hops(3).encrypted_recipient_data)),
)
)

assert(pathAliceToDave == expectedPath)

val sessionKey = PrivateKey(ByteVector32.fromValidHex((testVector \ "generate" \ "session_key").extract[String]))
val messageContent = TlvStream(Set.empty[OnionMessagePayloadTlv], getCustomTlvs(testVector \ "onionmessage"))
val sessionKey = PrivateKey(ByteVector32.fromValidHex(testVector.generate.session_key))
val messageContent = TlvStream(Set.empty[OnionMessagePayloadTlv], testVector.onionmessage.getCustomTlvs)
val Right(message) = buildMessage(sessionKey, blindingSecretAlice, intermediateAlice, BlindedPath(pathBobToDave), messageContent)
val encodedPacket = OnionRoutingCodecs.onionRoutingPacketCodec(1300).encode(message.onionRoutingPacket).require.bytes.toHex
val expectedPacket = (testVector \ "onionmessage" \ "onion_message_packet").extract[String]
assert(encodedPacket == expectedPacket)
val encodedPacket = OnionRoutingCodecs.onionRoutingPacketCodec(1300).encode(message.onionRoutingPacket).require.bytes
assert(encodedPacket.toHex == testVector.onionmessage.onion_message_packet)

// Checking that the onion is relayed properly
process(alice, message) match {
Expand Down

0 comments on commit 9762af8

Please sign in to comment.