From 5f82307e272837c00efb945849965d9ecec80801 Mon Sep 17 00:00:00 2001 From: GreyMcCarthy <105063806+GreyMcCarthy@users.noreply.github.com> Date: Mon, 13 Jun 2022 15:13:48 -0400 Subject: [PATCH] Added Compute Contract Id test Vectors (#4385) * Testing contract id calculation * Added implicit json reader imports --- .../commons/serializers/JsonSerializers.scala | 2 + .../core/protocol/dlc/compute/DLCUtil.scala | 25 +++++++-- .../src/test/resources/contract_id_test.json | 26 +++++++++ .../wallet/ComputeContractIdTest.scala | 56 +++++++++++++++++++ 4 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 wallet-test/src/test/resources/contract_id_test.json create mode 100644 wallet-test/src/test/scala/org/bitcoins/wallet/ComputeContractIdTest.scala diff --git a/app-commons/src/main/scala/org/bitcoins/commons/serializers/JsonSerializers.scala b/app-commons/src/main/scala/org/bitcoins/commons/serializers/JsonSerializers.scala index 19167dedee..b89ccf130b 100644 --- a/app-commons/src/main/scala/org/bitcoins/commons/serializers/JsonSerializers.scala +++ b/app-commons/src/main/scala/org/bitcoins/commons/serializers/JsonSerializers.scala @@ -44,6 +44,8 @@ object JsonSerializers { implicit val doubleSha256DigestBEReads: Reads[DoubleSha256DigestBE] = DoubleSha256DigestBEReads + implicit val sha256DigestReads: Reads[Sha256Digest] = Sha256DigestReads + implicit val ripeMd160DigestReads: Reads[RipeMd160Digest] = RipeMd160DigestReads diff --git a/core/src/main/scala/org/bitcoins/core/protocol/dlc/compute/DLCUtil.scala b/core/src/main/scala/org/bitcoins/core/protocol/dlc/compute/DLCUtil.scala index 22ecd115b0..bea34d25ab 100644 --- a/core/src/main/scala/org/bitcoins/core/protocol/dlc/compute/DLCUtil.scala +++ b/core/src/main/scala/org/bitcoins/core/protocol/dlc/compute/DLCUtil.scala @@ -27,6 +27,24 @@ import scala.util.Try object DLCUtil { + /** @see https://github.com/discreetlogcontracts/dlcspecs/blob/master/Protocol.md#definition-of-contract_id + * @param fundingTxId the id of the transaction that contains the DLC funding output + * @param outputIdx the index of the output + * @param tempContractId the temporary contractId in the offer message + * @return + */ + def computeContractId( + fundingTxId: DoubleSha256DigestBE, + outputIdx: Int, + tempContractId: Sha256Digest): ByteVector = { + val u16 = UInt16(outputIdx) + //we need to pad the u16 due to how xor works in scodec so we don't lose precision + val padded = ByteVector.fill(30)(0.toByte) ++ u16.bytes + fundingTxId.bytes + .xor(tempContractId.bytes) + .xor(padded) + } + /** @see https://github.com/discreetlogcontracts/dlcspecs/blob/master/Protocol.md#definition-of-contract_id * @param fundingTx the transaction that contains the DLC funding output * @param outputIdx the index of the output @@ -37,12 +55,7 @@ object DLCUtil { fundingTx: Transaction, outputIdx: Int, tempContractId: Sha256Digest): ByteVector = { - val u16 = UInt16(outputIdx) - //we need to pad the u16 due to how xor works in scodec so we don't lose precision - val padded = ByteVector.fill(30)(0.toByte) ++ u16.bytes - fundingTx.txIdBE.bytes - .xor(tempContractId.bytes) - .xor(padded) + computeContractId(fundingTx.txIdBE, outputIdx, tempContractId) } /** Extracts an adaptor secret from cetSig assuming it is the completion diff --git a/wallet-test/src/test/resources/contract_id_test.json b/wallet-test/src/test/resources/contract_id_test.json new file mode 100644 index 0000000000..972ea2c5db --- /dev/null +++ b/wallet-test/src/test/resources/contract_id_test.json @@ -0,0 +1,26 @@ +[ + { + "fundTxId": "22141d1118d666016f384cff3bf7fa124fe9ff0dcc206de88226219be5c6ee04", + "fundOutputIndex": 2, + "temporaryContractId": "925b5cbfdd7742d9e9ae0a6c869ce58ec56843ab539dd125a34a19661f3dabe9", + "contractId": "b04f41aec5a124d886964693bd6b1f9c8a81bca69fbdbccd216c38fdfafb45ef" + }, + { + "fundTxId": "67f25e18f0aca45196836508f5b3149fc900bd1709b32cf8f4d9f390744ba0b2", + "fundOutputIndex": 0, + "temporaryContractId": "83b848da05afbb4aa984731742ace0217cbc1a2bed72abfcdbd3059e171e78b4", + "contractId": "e44a16c2f5031f1b3f07161fb71ff4beb5bca73ce4c187042f0af60e6355d806" + }, + { + "fundTxId": "f10dc6857bb7562ef1920a5401c8332ac22adf12df695739b6afd5ed0ae2ee9b", + "fundOutputIndex": 2, + "temporaryContractId": "942bcce00a0a6ca1b41381e290fdcd68474ba6527131b81eeb2a143414fa4f4c", + "contractId": "65260a6571bd3a8f45818bb69135fe4285617940ae58ef275d85c1d91e18a1d5" + }, + { + "fundTxId": "75511322ccf0422f723ebc359bdde79f46134b046544bbfdd945dc1848cbc5c4", + "fundOutputIndex": 1, + "temporaryContractId": "c690e64ac286fc379c4b1f6fb69fb17626e589f50b6d6c2efc93ce8f299142b9", + "contractId": "b3c1f5680e76be18ee75a35a2d4256e960f6c2f16e29d7d325d61297615a877c" + } +] \ No newline at end of file diff --git a/wallet-test/src/test/scala/org/bitcoins/wallet/ComputeContractIdTest.scala b/wallet-test/src/test/scala/org/bitcoins/wallet/ComputeContractIdTest.scala new file mode 100644 index 0000000000..1ae833f6ca --- /dev/null +++ b/wallet-test/src/test/scala/org/bitcoins/wallet/ComputeContractIdTest.scala @@ -0,0 +1,56 @@ +package org.bitcoins.wallet + +import org.bitcoins.commons.serializers.JsonReaders.byteVectorReads +import org.bitcoins.commons.serializers.JsonSerializers._ +import org.bitcoins.core.protocol.dlc.compute.DLCUtil +import org.bitcoins.crypto.{DoubleSha256DigestBE, Sha256Digest} +import org.bitcoins.testkit.fixtures.EmptyFixture +import org.bitcoins.testkit.wallet.BitcoinSWalletTest +import play.api.libs.json._ +import scodec.bits.ByteVector +import scala.io.Source + +class ComputeContractIdTest extends BitcoinSWalletTest with EmptyFixture { + + lazy val json: JsValue = { + val stream = { + val classLoader = getClass().getClassLoader() + classLoader.getResourceAsStream("contract_id_test.json") + } + val rawText = Source + .fromInputStream(stream) + .getLines() + .mkString + Json.parse(rawText) + } + + case class ContractIdTestVector( + fundTxId: DoubleSha256DigestBE, + fundOutputIndex: Int, + temporaryContractId: Sha256Digest, + contractId: ByteVector) + + object ContractIdTestVector { + + implicit val reads: Reads[ContractIdTestVector] = + Json.reads[ContractIdTestVector] + } + + lazy val vectors: Vector[ContractIdTestVector] = + json.validate[Vector[ContractIdTestVector]] match { + case JsError(errors) => fail(errors.head.toString) + case JsSuccess(value, _) => value + } + + "DLCUtil" must "compute contract ids correctly as in the JSON file" in { _ => + vectors.foreach { testVector => + assert( + DLCUtil.computeContractId( + testVector.fundTxId, + testVector.fundOutputIndex, + testVector.temporaryContractId) == testVector.contractId) + } + + succeed + } +}