2024-08-18 01:27:04 +02:00
|
|
|
package fr.acinq.secp256k1
|
|
|
|
|
|
|
|
import kotlinx.serialization.json.*
|
|
|
|
|
|
|
|
import kotlin.test.*
|
|
|
|
|
|
|
|
class FrostTest: BaseTest() {
|
|
|
|
|
|
|
|
// Frost Share Generation
|
|
|
|
@Test
|
|
|
|
fun `frost share generation`() {
|
|
|
|
val tests = readData("frost/share_gen_vectors.json")
|
|
|
|
val pubkeys = tests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
|
2024-08-19 21:40:56 +02:00
|
|
|
tests.jsonObject["valid_share_gen_test_cases"]!!.jsonArray.forEach { validTestCases ->
|
|
|
|
|
2024-08-18 01:27:04 +02:00
|
|
|
val keyIndices = validTestCases.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
|
|
|
|
2024-08-19 21:40:56 +02:00
|
|
|
val seed32 = Hex.decode(validTestCases.jsonObject["seed"]!!.jsonPrimitive.content)
|
2024-08-18 01:27:04 +02:00
|
|
|
val nParticipants = keyIndices.size
|
|
|
|
val threshold = validTestCases.jsonObject["threshold"]!!.jsonPrimitive.int
|
|
|
|
val ids33 = keyIndices.map { pubkeys[it] }.toTypedArray()
|
|
|
|
|
|
|
|
val result = Secp256k1.frostSharesGen(
|
|
|
|
seed32,
|
|
|
|
threshold,
|
|
|
|
nParticipants,
|
|
|
|
ids33
|
|
|
|
)
|
|
|
|
|
|
|
|
val expected = validTestCases.jsonObject["expected"]!!;
|
|
|
|
|
|
|
|
val expectedShare = expected.jsonObject["frost_share"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
val expectedVSSCommitment = expected.jsonObject["vss_commitment"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
val expectedPoK64 = Hex.decode(expected.jsonObject["pok64"]!!.jsonPrimitive.content)
|
|
|
|
|
|
|
|
result.first.forEachIndexed { index, share ->
|
|
|
|
assertEquals(
|
|
|
|
expected = Hex.encode(expectedShare[index]),
|
|
|
|
actual = Hex.encode(share),
|
|
|
|
"Unexpected $index:share for $keyIndices test case"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
result.second.forEachIndexed { index, vssCommitment ->
|
|
|
|
assertEquals(
|
|
|
|
expected = Hex.encode(expectedVSSCommitment[index]),
|
|
|
|
actual = Hex.encode(vssCommitment),
|
|
|
|
"Unexpected $index:vss_commitment for the $keyIndices test case"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
assertEquals(
|
|
|
|
expected = Hex.encode(expectedPoK64),
|
|
|
|
actual = Hex.encode(result.third),
|
|
|
|
message = "Unexpected pok64 for $keyIndices test case"
|
|
|
|
)
|
|
|
|
}
|
2024-08-19 21:40:56 +02:00
|
|
|
val signerShareGenTestCase = tests.jsonObject["valid_signers_share_gen_test_case"]!!
|
|
|
|
|
|
|
|
val keyIndices = signerShareGenTestCase.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
|
|
|
|
|
|
|
val nParticipants = keyIndices.size
|
|
|
|
val threshold = signerShareGenTestCase.jsonObject["threshold"]!!.jsonPrimitive.int
|
|
|
|
val ids33 = keyIndices.map { pubkeys[it] }.toTypedArray()
|
|
|
|
|
|
|
|
signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.forEachIndexed { signerIndex, signer ->
|
|
|
|
val seed32 = Hex.decode(signer.jsonObject["seed"]!!.jsonPrimitive.content)
|
|
|
|
|
|
|
|
val result = Secp256k1.frostSharesGen(
|
|
|
|
seed32,
|
|
|
|
threshold,
|
|
|
|
nParticipants,
|
|
|
|
ids33
|
|
|
|
)
|
|
|
|
|
|
|
|
val expected = signer.jsonObject["expected"]!!;
|
|
|
|
|
|
|
|
val expectedShare = expected.jsonObject["frost_share"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
val expectedVSSCommitment = expected.jsonObject["vss_commitment"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
val expectedPoK64 = Hex.decode(expected.jsonObject["pok64"]!!.jsonPrimitive.content)
|
|
|
|
|
|
|
|
result.first.forEachIndexed { index, share ->
|
|
|
|
assertEquals(
|
|
|
|
expected = Hex.encode(expectedShare[index]),
|
|
|
|
actual = Hex.encode(share),
|
|
|
|
"Unexpected $signerIndex:signer $index:share for $keyIndices test case"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
result.second.forEachIndexed { index, vssCommitment ->
|
|
|
|
assertEquals(
|
|
|
|
expected = Hex.encode(expectedVSSCommitment[index]),
|
|
|
|
actual = Hex.encode(vssCommitment),
|
|
|
|
"Unexpected $signerIndex:signer $index:vss_commitment for the $keyIndices test case"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
assertEquals(
|
|
|
|
expected = Hex.encode(expectedPoK64),
|
|
|
|
actual = Hex.encode(result.third),
|
|
|
|
message = "Unexpected $signerIndex:signer pok64 for $keyIndices test case"
|
|
|
|
)
|
|
|
|
}
|
2024-08-18 01:27:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost share aggregation`() {
|
2024-08-19 21:40:56 +02:00
|
|
|
val shareGenTests = readData("frost/share_gen_vectors.json")
|
|
|
|
val tests = readData("frost/share_agg_vectors.json")
|
|
|
|
|
|
|
|
val publicKeys = shareGenTests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
|
|
|
|
val signerShareGenTestCase = shareGenTests.jsonObject["valid_signers_share_gen_test_case"]!!;
|
|
|
|
|
|
|
|
val keyIndices = signerShareGenTestCase.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
|
|
|
val nParticipants = keyIndices.size
|
|
|
|
val threshold = signerShareGenTestCase.jsonObject["threshold"]!!.jsonPrimitive.int
|
|
|
|
val ids33 = keyIndices.map { publicKeys[it] }.toTypedArray()
|
|
|
|
|
|
|
|
val vssCommitments = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map { signer ->
|
|
|
|
signer.jsonObject["expected"]!!.jsonObject["vss_commitment"]!!.jsonArray.map {
|
|
|
|
Hex.decode(it.jsonPrimitive.content)
|
|
|
|
}.toTypedArray()
|
|
|
|
}
|
|
|
|
|
2024-08-20 01:05:29 +02:00
|
|
|
signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.forEachIndexed { index, _ ->
|
2024-08-19 21:40:56 +02:00
|
|
|
val assignedShares = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map {
|
|
|
|
Hex.decode(
|
|
|
|
it.jsonObject["expected"]!!.jsonObject["frost_share"]!!.jsonArray[index].jsonPrimitive.content
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
val result = Secp256k1.frostShareAggregate(
|
|
|
|
assignedShares.toTypedArray(),
|
|
|
|
vssCommitments.toTypedArray(),
|
|
|
|
nParticipants,
|
|
|
|
threshold,
|
|
|
|
ids33[index]
|
|
|
|
)
|
|
|
|
|
|
|
|
val expected = tests.jsonObject["expected"]!!.jsonArray[index];
|
|
|
|
|
|
|
|
val expectedAggregateShare = expected.jsonObject["aggregate_share"]!!.jsonPrimitive.content
|
|
|
|
val expectedPublicKey = expected.jsonObject["aggregate_public_key"]!!.jsonPrimitive.content
|
2024-08-20 01:05:29 +02:00
|
|
|
val expectedPublicShare = expected.jsonObject["public_share"]!!.jsonPrimitive.content
|
2024-08-19 21:40:56 +02:00
|
|
|
|
2024-08-20 00:09:56 +02:00
|
|
|
assertEquals(
|
|
|
|
expected = expectedAggregateShare,
|
|
|
|
actual = Hex.encode(result.first),
|
|
|
|
"Unexpected $index:aggregate_share"
|
2024-08-19 21:40:56 +02:00
|
|
|
)
|
2024-08-20 00:09:56 +02:00
|
|
|
assertEquals(
|
|
|
|
expected = expectedPublicKey,
|
|
|
|
actual = Hex.encode(result.second),
|
|
|
|
"Unexpected $index:aggregate_public_key"
|
2024-08-19 21:40:56 +02:00
|
|
|
)
|
|
|
|
|
2024-08-20 01:05:29 +02:00
|
|
|
assertEquals(
|
|
|
|
expected = 1,
|
|
|
|
actual = Secp256k1.frostShareVerify(
|
|
|
|
threshold,
|
|
|
|
ids33[index],
|
|
|
|
assignedShares[index],
|
|
|
|
vssCommitments[index]
|
|
|
|
),
|
|
|
|
message = "Couldn't verify share from $index signer"
|
|
|
|
)
|
2024-08-19 21:40:56 +02:00
|
|
|
|
2024-08-20 01:05:29 +02:00
|
|
|
assertEquals(
|
|
|
|
expected = expectedPublicShare,
|
|
|
|
actual = Hex.encode(
|
|
|
|
Secp256k1.frostComputePublicShare(
|
|
|
|
threshold,
|
|
|
|
ids33[index],
|
|
|
|
vssCommitments.toTypedArray(),
|
|
|
|
nParticipants
|
|
|
|
)
|
|
|
|
),
|
|
|
|
message = "Couldn't verify share from $index signer"
|
|
|
|
)
|
|
|
|
}
|
2024-08-18 01:27:04 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost share verify`() {
|
2024-08-20 01:05:29 +02:00
|
|
|
val shareGenTests = readData("frost/share_gen_vectors.json")
|
|
|
|
val tests = readData("frost/share_agg_vectors.json")
|
|
|
|
|
|
|
|
val publicKeys = shareGenTests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
|
|
|
|
val signerShareGenTestCase = shareGenTests.jsonObject["valid_signers_share_gen_test_case"]!!;
|
|
|
|
|
|
|
|
val keyIndices = signerShareGenTestCase.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
|
|
|
val nParticipants = keyIndices.size
|
|
|
|
val threshold = signerShareGenTestCase.jsonObject["threshold"]!!.jsonPrimitive.int
|
|
|
|
val ids33 = keyIndices.map { publicKeys[it] }.toTypedArray()
|
|
|
|
|
|
|
|
val vssCommitments = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map { signer ->
|
|
|
|
signer.jsonObject["expected"]!!.jsonObject["vss_commitment"]!!.jsonArray.map {
|
|
|
|
Hex.decode(it.jsonPrimitive.content)
|
|
|
|
}.toTypedArray()
|
|
|
|
}
|
|
|
|
|
|
|
|
signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.forEachIndexed { index, _ ->
|
|
|
|
val assignedShares = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map {
|
|
|
|
Hex.decode(
|
|
|
|
it.jsonObject["expected"]!!.jsonObject["frost_share"]!!.jsonArray[index].jsonPrimitive.content
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
val result = Secp256k1.frostShareAggregate(
|
|
|
|
assignedShares.toTypedArray(),
|
|
|
|
vssCommitments.toTypedArray(),
|
|
|
|
nParticipants,
|
|
|
|
threshold,
|
|
|
|
ids33[index]
|
|
|
|
)
|
|
|
|
|
|
|
|
val expected = tests.jsonObject["expected"]!!.jsonArray[index];
|
|
|
|
|
|
|
|
val expectedAggregateShare = expected.jsonObject["aggregate_share"]!!.jsonPrimitive.content
|
|
|
|
val expectedPublicKey = expected.jsonObject["aggregate_public_key"]!!.jsonPrimitive.content
|
|
|
|
val expectedPublicShare = expected.jsonObject["public_share"]!!.jsonPrimitive.content
|
2024-08-18 01:27:04 +02:00
|
|
|
|
2024-08-20 01:05:29 +02:00
|
|
|
assertEquals(
|
|
|
|
expected = expectedAggregateShare,
|
|
|
|
actual = Hex.encode(result.first),
|
|
|
|
"Unexpected $index:aggregate_share"
|
|
|
|
)
|
|
|
|
assertEquals(
|
|
|
|
expected = expectedPublicKey,
|
|
|
|
actual = Hex.encode(result.second),
|
|
|
|
"Unexpected $index:aggregate_public_key"
|
|
|
|
)
|
|
|
|
|
|
|
|
assertEquals(
|
|
|
|
expected = 1,
|
|
|
|
actual = Secp256k1.frostShareVerify(
|
|
|
|
threshold,
|
|
|
|
ids33[index],
|
|
|
|
assignedShares[index],
|
|
|
|
vssCommitments[index]
|
|
|
|
),
|
|
|
|
message = "Couldn't verify share from $index signer"
|
|
|
|
)
|
|
|
|
|
|
|
|
assertEquals(
|
|
|
|
expected = expectedPublicShare,
|
|
|
|
actual = Hex.encode(
|
|
|
|
Secp256k1.frostComputePublicShare(
|
|
|
|
threshold,
|
|
|
|
ids33[index],
|
|
|
|
vssCommitments.toTypedArray(),
|
|
|
|
nParticipants
|
|
|
|
)
|
|
|
|
),
|
|
|
|
message = "Couldn't verify share from $index signer"
|
|
|
|
)
|
|
|
|
}
|
2024-08-18 01:27:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost compute pubshare`() {
|
2024-08-20 01:05:29 +02:00
|
|
|
val shareGenTests = readData("frost/share_gen_vectors.json")
|
|
|
|
val tests = readData("frost/share_agg_vectors.json")
|
|
|
|
|
|
|
|
val publicKeys = shareGenTests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
2024-08-18 01:27:04 +02:00
|
|
|
|
2024-08-20 01:05:29 +02:00
|
|
|
val signerShareGenTestCase = shareGenTests.jsonObject["valid_signers_share_gen_test_case"]!!;
|
|
|
|
|
|
|
|
val keyIndices = signerShareGenTestCase.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
|
|
|
val nParticipants = keyIndices.size
|
|
|
|
val threshold = signerShareGenTestCase.jsonObject["threshold"]!!.jsonPrimitive.int
|
|
|
|
val ids33 = keyIndices.map { publicKeys[it] }.toTypedArray()
|
|
|
|
|
|
|
|
val vssCommitments = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map { signer ->
|
|
|
|
signer.jsonObject["expected"]!!.jsonObject["vss_commitment"]!!.jsonArray.map {
|
|
|
|
Hex.decode(it.jsonPrimitive.content)
|
|
|
|
}.toTypedArray()
|
|
|
|
}
|
|
|
|
|
|
|
|
signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.forEachIndexed { index, _ ->
|
|
|
|
|
|
|
|
val expected = tests.jsonObject["expected"]!!.jsonArray[index];
|
|
|
|
|
|
|
|
val expectedPublicShare = expected.jsonObject["public_share"]!!.jsonPrimitive.content
|
|
|
|
|
|
|
|
assertEquals(
|
|
|
|
expected = expectedPublicShare,
|
|
|
|
actual = Hex.encode(
|
|
|
|
Secp256k1.frostComputePublicShare(
|
|
|
|
threshold,
|
|
|
|
ids33[index],
|
|
|
|
vssCommitments.toTypedArray(),
|
|
|
|
nParticipants
|
|
|
|
)
|
|
|
|
),
|
|
|
|
message = "Couldn't verify share from $index signer"
|
|
|
|
)
|
|
|
|
}
|
2024-08-18 01:27:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Frost Tweak
|
|
|
|
@Test
|
|
|
|
fun `frost pubkey tweak`() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost pubkey ec tweak add`() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost pubkey xonly tweak add`() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Frost Sign functionality
|
|
|
|
@Test
|
|
|
|
fun `frost nonce gen`() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost nonce process`() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost partial sign `() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost partial signature verify`() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost partial signature aggregation`() {
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|