From 3770891331a701ac669531419647cca627c6e386 Mon Sep 17 00:00:00 2001 From: George Digkas Date: Sun, 28 Jan 2024 18:19:49 +0200 Subject: [PATCH 1/5] order PKs --- move/sources/multisig.move | 123 +++++++++++++++++++++++++++++++++ move/tests/multisig_tests.move | 90 +++++++++++++++++++++--- 2 files changed, 203 insertions(+), 10 deletions(-) diff --git a/move/sources/multisig.move b/move/sources/multisig.move index c00beb5..36a5aef 100644 --- a/move/sources/multisig.move +++ b/move/sources/multisig.move @@ -16,6 +16,8 @@ module multisig::multisig { /// Error code indicating that the threshold is positive and not greater than the sum of weights. const EThresholdIsPositiveAndNotGreaterThanTheSumOfWeights: u64 = 1; + const ENoPermutationMatchesTheExpectedAddress: u64 = 2; + /// Event emitted when a multisig address is created. struct MultisigAddressEvent has copy, drop { pks: vector>, @@ -162,4 +164,125 @@ module multisig::multisig { address::from_bytes(sui::hash::blake2b256(pk)) } + /// This function orders the public keys (pks) in all possible permutations and checks if the derived multisig address matches the expected multisig address. + /// It takes the expected multisig address, a vector of public keys (pks), a vector of weights corresponding to the public keys, and a threshold value as input. + /// The function returns the ordered public keys (pks) if a permutation matches the expected multisig address. + /// If no permutation matches the expected multisig address, it aborts with an error. + /// + /// Parameters: + /// - extected_ms_address: The expected multisig address to match. + /// - pks: A vector of vectors containing the public keys. + /// - weights: A vector of weights corresponding to the public keys. + /// - threshold: The threshold value for multisig. + /// + /// Returns: + /// - A vector of vectors containing the ordered public keys (pks) if a permutation matches the expected multisig address. + /// + /// Abort: + /// - ENoPermutationMatchesTheExpectedAddress: If no permutation matches the expected multisig address. + public fun order_pks( + extected_ms_address: address, + pks: vector>, + weights: vector, + threshold: u16, + ): vector> { + // loop through all the permutations of the pks vector + let perms = permutations(pks); + let n = vector::length(&perms); + let i = 0; + while (i < n) { + let perm: vector> = *vector::borrow(&perms, i); + let ms_address = derive_multisig_address_quiet(perm, weights, threshold); + // check if the ms_address matches the expected one + if (ms_address == extected_ms_address) { + return perm + }; + i = i + 1; + }; + + abort ENoPermutationMatchesTheExpectedAddress + } + + /// Generates all possible permutations of a vector of vectors. + /// + /// This function takes a vector of vectors `pks` as input and generates all possible permutations of the vectors in `pks`. + /// The function uses a modified version of the Heap's algorithm to generate the permutations. + /// It initializes an empty vector `perms` to store the permutations and a vector `c` to encode the stack state. + /// The function iterates through the vectors in `pks` and swaps elements based on the parity of the iteration index. + /// It outputs each new permutation and increments the stack state accordingly. + /// Finally, it returns the vector `perms` containing all the generated permutations. + /// + /// # Arguments + /// + /// * `pks` - A vector of vectors representing the input vectors to generate permutations for. + /// + /// # Returns + /// + /// A vector of vectors representing all possible permutations of the input vectors. + /// + /// # Examples + /// + /// ``` + /// let pks = vector[ + /// vector[1, 2, 3], + /// vector[4, 5], + /// vector[6, 7, 8, 9] + /// ]; + /// + /// let perms = permutations(pks); + /// + /// assert!(vector::length(&perms) == 6, 0); + /// assert!(*vector::borrow(&perms, 0) == vector[1, 2, 3], 0); + /// assert!(*vector::borrow(&perms, 1) == vector[4, 5], 0); + /// assert!(*vector::borrow(&perms, 2) == vector[6, 7, 8, 9], 0); + /// assert!(*vector::borrow(&perms, 3) == vector[2, 1, 3], 0); + /// assert!(*vector::borrow(&perms, 4) == vector[5, 4], 0); + /// assert!(*vector::borrow(&perms, 5) == vector[7, 6, 8, 9], 0); + /// ``` + public fun permutations( + pks: vector>, + ): vector>> { + // initialize an empty vector to store the permutations + let perms = vector::empty>>(); + // get the length of the pks vector + let n = vector::length(&pks); + // c is an encoding of the stack state. c[k] encodes the for-loop counter for when permutations(k - 1, pks) is called + let c = vector::empty(); + // initialize c with zeros + let i = 0; + while (i < n) { + vector::push_back(&mut c, 0); + i = i + 1 + }; + // output the first permutation + vector::push_back(&mut perms, pks); + // i acts similarly to a stack pointer + i = 1; + // loop until i is equal to n + while (i < n) { + // check if c[i] is less than i + if (*vector::borrow(&c, i) < i) { + // swap elements depending on the parity of i + if (i % 2 == 0) { + vector::swap(&mut pks, 0, i); + } else { + vector::swap(&mut pks, *vector::borrow(&c, (i as u64)), i); + }; + // output the new permutation + vector::push_back(&mut perms, pks); + // increment c[i] by 1 + *vector::borrow_mut(&mut c, i) = *vector::borrow(&c, i) + 1; + // reset i to 1 + i = 1; + } else { + // reset c[i] to 0, c[i] = 0; + *vector::borrow_mut(&mut c, i) = 0; + // increment i by 1 + i = i + 1; + }; + }; + // return the perms vector + perms + } + } diff --git a/move/tests/multisig_tests.move b/move/tests/multisig_tests.move index 8c05d61..8c3f5dc 100644 --- a/move/tests/multisig_tests.move +++ b/move/tests/multisig_tests.move @@ -3,26 +3,27 @@ #[test_only] module multisig::multisig_unit_tests { + use std::vector; use multisig::multisig::{Self as ms}; #[test] fun test_derive_multisig_address() { // ED25519 - let address_ED25519: address = @0x73a6b3c33e2d63383de5c6786cbaca231ff789f4c853af6d54cb883d8780adc0; - let key_ED25519: vector = vector[0, 13, 125, 171, 53, 140, 141, 173, 170, 78, 250, 0, 73, 167, 91, 7, 67, 101, 85, 177, 10, 54, 130, 25, 187, 104, 15, 112, 87, 19, 73, 215, 117]; - assert!(ms::ed25519_key_to_address(&key_ED25519) == address_ED25519, 0); + let ed25519_address_: address = @0x73a6b3c33e2d63383de5c6786cbaca231ff789f4c853af6d54cb883d8780adc0; + let ed25519_key: vector = vector[0, 13, 125, 171, 53, 140, 141, 173, 170, 78, 250, 0, 73, 167, 91, 7, 67, 101, 85, 177, 10, 54, 130, 25, 187, 104, 15, 112, 87, 19, 73, 215, 117]; + assert!(ms::ed25519_key_to_address(&ed25519_key) == ed25519_address_, 0); // Secp256k1 - let address_Secp256k1: address = @0xd9607cd03428c904949572b51471e7a9f60019aeb9a3d7ee5e72921cab8e8be7; - let key_Secp256k1: vector = vector[1, 2, 14, 23, 205, 89, 57, 228, 107, 25, 102, 65, 150, 140, 215, 89, 145, 11, 162, 87, 126, 39, 250, 115, 253, 227, 135, 109, 185, 190, 197, 188, 235, 43]; - assert!(ms::secp256k1_key_to_address(&key_Secp256k1) == address_Secp256k1, 0); + let secp256k1_address_: address = @0xd9607cd03428c904949572b51471e7a9f60019aeb9a3d7ee5e72921cab8e8be7; + let secp256k1_key: vector = vector[1, 2, 14, 23, 205, 89, 57, 228, 107, 25, 102, 65, 150, 140, 215, 89, 145, 11, 162, 87, 126, 39, 250, 115, 253, 227, 135, 109, 185, 190, 197, 188, 235, 43]; + assert!(ms::secp256k1_key_to_address(&secp256k1_key) == secp256k1_address_, 0); // Secp256r1 - let address_Secp256r1: address = @0x600b1081644fe46f76da3bdc19f8743b9f04458516364374c7d82959e790c19e; - let key_Secp256r1: vector = vector[2, 3, 71, 251, 175, 35, 240, 56, 171, 196, 195, 8, 162, 113, 17, 122, 42, 76, 255, 174, 221, 188, 95, 248, 28, 117, 23, 188, 108, 116, 167, 237, 180, 48]; - assert!(ms::secp256r1_key_to_address(&key_Secp256r1) == address_Secp256r1, 0); + let secp256r1_address: address = @0x600b1081644fe46f76da3bdc19f8743b9f04458516364374c7d82959e790c19e; + let secp256r1_key: vector = vector[2, 3, 71, 251, 175, 35, 240, 56, 171, 196, 195, 8, 162, 113, 17, 122, 42, 76, 255, 174, 221, 188, 95, 248, 28, 117, 23, 188, 108, 116, 167, 237, 180, 48]; + assert!(ms::secp256r1_key_to_address(&secp256r1_key) == secp256r1_address, 0); - let pks: vector> = vector[key_ED25519, key_Secp256k1, key_Secp256r1]; + let pks: vector> = vector[ed25519_key, secp256k1_key, secp256r1_key]; let weights: vector = vector[1, 1, 1]; let threshold: u16 = 2; @@ -31,4 +32,73 @@ module multisig::multisig_unit_tests { assert!(derived_multisig_address == expected_multisig_address, 0); } + #[test] + fun test_permutations() { + let ed25519_key: vector = vector[0, 13, 125, 171, 53, 140, 141, 173, 170, 78, 250, 0, 73, 167, 91, 7, 67, 101, 85, 177, 10, 54, 130, 25, 187, 104, 15, 112, 87, 19, 73, 215, 117]; + let secp256k1_key: vector = vector[1, 2, 14, 23, 205, 89, 57, 228, 107, 25, 102, 65, 150, 140, 215, 89, 145, 11, 162, 87, 126, 39, 250, 115, 253, 227, 135, 109, 185, 190, 197, 188, 235, 43]; + let secp256r1_key: vector = vector[2, 3, 71, 251, 175, 35, 240, 56, 171, 196, 195, 8, 162, 113, 17, 122, 42, 76, 255, 174, 221, 188, 95, 248, 28, 117, 23, 188, 108, 116, 167, 237, 180, 48]; + + let pks: vector> = vector[ed25519_key, secp256k1_key, secp256r1_key]; + let permutations: vector>> = ms::permutations(pks); + + assert!(vector::length(&permutations) == 6, 0); + assert!(*vector::borrow(&permutations, 0) == vector[ed25519_key, secp256k1_key, secp256r1_key], 0); + assert!(*vector::borrow(&permutations, 1) == vector[secp256k1_key, ed25519_key, secp256r1_key], 0); + assert!(*vector::borrow(&permutations, 2) == vector[secp256r1_key, ed25519_key, secp256k1_key], 0); + assert!(*vector::borrow(&permutations, 3) == vector[ed25519_key, secp256r1_key, secp256k1_key], 0); + assert!(*vector::borrow(&permutations, 4) == vector[secp256k1_key, secp256r1_key, ed25519_key], 0); + assert!(*vector::borrow(&permutations, 5) == vector[secp256r1_key, secp256k1_key, ed25519_key], 0); + } + + #[test] + fun test_order_pks() { + let ed25519_key: vector = vector[0, 13, 125, 171, 53, 140, 141, 173, 170, 78, 250, 0, 73, 167, 91, 7, 67, 101, 85, 177, 10, 54, 130, 25, 187, 104, 15, 112, 87, 19, 73, 215, 117]; + let secp256k1_key: vector = vector[1, 2, 14, 23, 205, 89, 57, 228, 107, 25, 102, 65, 150, 140, 215, 89, 145, 11, 162, 87, 126, 39, 250, 115, 253, 227, 135, 109, 185, 190, 197, 188, 235, 43]; + let secp256r1_key: vector = vector[2, 3, 71, 251, 175, 35, 240, 56, 171, 196, 195, 8, 162, 113, 17, 122, 42, 76, 255, 174, 221, 188, 95, 248, 28, 117, 23, 188, 108, 116, 167, 237, 180, 48]; + + let pks0: vector> = vector[ed25519_key, secp256k1_key, secp256r1_key]; + let pks1: vector> = vector[secp256k1_key, ed25519_key, secp256r1_key]; + let pks2: vector> = vector[secp256r1_key, ed25519_key, secp256k1_key]; + let pks3: vector> = vector[ed25519_key, secp256r1_key, secp256k1_key]; + let pks4: vector> = vector[secp256k1_key, secp256r1_key, ed25519_key]; + let pks5: vector> = vector[secp256r1_key, secp256k1_key, ed25519_key]; + + let ordered_pks: vector> = vector[ed25519_key, secp256k1_key, secp256r1_key]; + let weights: vector = vector[1, 1, 1]; + let threshold: u16 = 2; + let multisig_address: address = @0x1c4dac7fb4c01a0c608db993711c451ad655a38b7f0a9571ff099f70090263a8; + + assert!(ms::order_pks(multisig_address, pks0, weights, threshold) == ordered_pks, 0); + assert!(ms::order_pks(multisig_address, pks1, weights, threshold) == ordered_pks, 0); + assert!(ms::order_pks(multisig_address, pks2, weights, threshold) == ordered_pks, 0); + assert!(ms::order_pks(multisig_address, pks3, weights, threshold) == ordered_pks, 0); + assert!(ms::order_pks(multisig_address, pks4, weights, threshold) == ordered_pks, 0); + assert!(ms::order_pks(multisig_address, pks5, weights, threshold) == ordered_pks, 0); + } + + #[test] + #[expected_failure(abort_code = ms::ENoPermutationMatchesTheExpectedAddress)] + fun test_failure_order_pks() { + let ed25519_key: vector = vector[0, 13, 125, 171, 53, 140, 141, 173, 170, 78, 250, 0, 73, 167, 91, 7, 67, 101, 85, 177, 10, 54, 130, 25, 187, 104, 15, 112, 87, 19, 73, 215, 117]; + let secp256k1_key: vector = vector[1, 2, 14, 23, 205, 89, 57, 228, 107, 25, 102, 65, 150, 140, 215, 89, 145, 11, 162, 87, 126, 39, 250, 115, 253, 227, 135, 109, 185, 190, 197, 188, 235, 43]; + let secp256r1_key: vector = vector[2, 3, 71, 251, 175, 35, 240, 56, 171, 196, 195, 8, 162, 113, 17, 122, 42, 76, 255, 174, 221, 188, 95, 248, 28, 117, 23, 188, 108, 116, 167, 237, 180, 48]; + + let pks0: vector> = vector[ed25519_key, secp256k1_key, secp256r1_key]; + let pks1: vector> = vector[secp256k1_key, ed25519_key, secp256r1_key]; + let pks2: vector> = vector[secp256r1_key, ed25519_key, secp256k1_key]; + let pks3: vector> = vector[ed25519_key, secp256r1_key, secp256k1_key]; + let pks4: vector> = vector[secp256k1_key, secp256r1_key, ed25519_key]; + let pks5: vector> = vector[secp256r1_key, secp256k1_key, ed25519_key]; + + let weights: vector = vector[1, 1, 1]; + let threshold: u16 = 2; + let multisig_address: address = @0xCAFE; + + ms::order_pks(multisig_address, pks0, weights, threshold); + ms::order_pks(multisig_address, pks1, weights, threshold); + ms::order_pks(multisig_address, pks2, weights, threshold); + ms::order_pks(multisig_address, pks3, weights, threshold); + ms::order_pks(multisig_address, pks4, weights, threshold); + ms::order_pks(multisig_address, pks5, weights, threshold); + } } From 40b729f7d4ef6af4f44978776ae27f90d416f506 Mon Sep 17 00:00:00 2001 From: George Digkas Date: Sun, 28 Jan 2024 18:24:06 +0200 Subject: [PATCH 2/5] add code comment --- move/sources/multisig.move | 1 + 1 file changed, 1 insertion(+) diff --git a/move/sources/multisig.move b/move/sources/multisig.move index 36a5aef..6d5bb38 100644 --- a/move/sources/multisig.move +++ b/move/sources/multisig.move @@ -16,6 +16,7 @@ module multisig::multisig { /// Error code indicating that the threshold is positive and not greater than the sum of weights. const EThresholdIsPositiveAndNotGreaterThanTheSumOfWeights: u64 = 1; + /// Error code indicating that no permutation matches the expected multisig address. const ENoPermutationMatchesTheExpectedAddress: u64 = 2; /// Event emitted when a multisig address is created. From 4ac31204d3935a0ea50be6bf83bab83a207102ff Mon Sep 17 00:00:00 2001 From: George Digkas Date: Mon, 29 Jan 2024 10:37:31 +0200 Subject: [PATCH 3/5] refactor code --- move/sources/multisig.move | 85 +------------------------------- move/sources/utils.move | 90 ++++++++++++++++++++++++++++++++++ move/tests/multisig_tests.move | 19 ------- move/tests/utils_test.move | 26 ++++++++++ 4 files changed, 118 insertions(+), 102 deletions(-) create mode 100644 move/sources/utils.move create mode 100644 move/tests/utils_test.move diff --git a/move/sources/multisig.move b/move/sources/multisig.move index 6d5bb38..99235b3 100644 --- a/move/sources/multisig.move +++ b/move/sources/multisig.move @@ -9,6 +9,7 @@ module multisig::multisig { use sui::hash::blake2b256; use sui::tx_context::{Self, TxContext}; use std::vector; + use multisig::utils; /// Error code indicating that the lengths of public keys and weights are not equal. const ELengthsOfPksAndWeightsAreNotEqual: u64 = 0; @@ -188,7 +189,7 @@ module multisig::multisig { threshold: u16, ): vector> { // loop through all the permutations of the pks vector - let perms = permutations(pks); + let perms = utils::permutations(pks); let n = vector::length(&perms); let i = 0; while (i < n) { @@ -204,86 +205,4 @@ module multisig::multisig { abort ENoPermutationMatchesTheExpectedAddress } - /// Generates all possible permutations of a vector of vectors. - /// - /// This function takes a vector of vectors `pks` as input and generates all possible permutations of the vectors in `pks`. - /// The function uses a modified version of the Heap's algorithm to generate the permutations. - /// It initializes an empty vector `perms` to store the permutations and a vector `c` to encode the stack state. - /// The function iterates through the vectors in `pks` and swaps elements based on the parity of the iteration index. - /// It outputs each new permutation and increments the stack state accordingly. - /// Finally, it returns the vector `perms` containing all the generated permutations. - /// - /// # Arguments - /// - /// * `pks` - A vector of vectors representing the input vectors to generate permutations for. - /// - /// # Returns - /// - /// A vector of vectors representing all possible permutations of the input vectors. - /// - /// # Examples - /// - /// ``` - /// let pks = vector[ - /// vector[1, 2, 3], - /// vector[4, 5], - /// vector[6, 7, 8, 9] - /// ]; - /// - /// let perms = permutations(pks); - /// - /// assert!(vector::length(&perms) == 6, 0); - /// assert!(*vector::borrow(&perms, 0) == vector[1, 2, 3], 0); - /// assert!(*vector::borrow(&perms, 1) == vector[4, 5], 0); - /// assert!(*vector::borrow(&perms, 2) == vector[6, 7, 8, 9], 0); - /// assert!(*vector::borrow(&perms, 3) == vector[2, 1, 3], 0); - /// assert!(*vector::borrow(&perms, 4) == vector[5, 4], 0); - /// assert!(*vector::borrow(&perms, 5) == vector[7, 6, 8, 9], 0); - /// ``` - public fun permutations( - pks: vector>, - ): vector>> { - // initialize an empty vector to store the permutations - let perms = vector::empty>>(); - // get the length of the pks vector - let n = vector::length(&pks); - // c is an encoding of the stack state. c[k] encodes the for-loop counter for when permutations(k - 1, pks) is called - let c = vector::empty(); - // initialize c with zeros - let i = 0; - while (i < n) { - vector::push_back(&mut c, 0); - i = i + 1 - }; - // output the first permutation - vector::push_back(&mut perms, pks); - // i acts similarly to a stack pointer - i = 1; - // loop until i is equal to n - while (i < n) { - // check if c[i] is less than i - if (*vector::borrow(&c, i) < i) { - // swap elements depending on the parity of i - if (i % 2 == 0) { - vector::swap(&mut pks, 0, i); - } else { - vector::swap(&mut pks, *vector::borrow(&c, (i as u64)), i); - }; - // output the new permutation - vector::push_back(&mut perms, pks); - // increment c[i] by 1 - *vector::borrow_mut(&mut c, i) = *vector::borrow(&c, i) + 1; - // reset i to 1 - i = 1; - } else { - // reset c[i] to 0, c[i] = 0; - *vector::borrow_mut(&mut c, i) = 0; - // increment i by 1 - i = i + 1; - }; - }; - // return the perms vector - perms - } - } diff --git a/move/sources/utils.move b/move/sources/utils.move new file mode 100644 index 0000000..401aa21 --- /dev/null +++ b/move/sources/utils.move @@ -0,0 +1,90 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// This module contains the permutations functions for generating all possible permutations of a vector of vectors. +module multisig::utils { + use std::vector; + + /// Generates all possible permutations of a vector of vectors. + /// + /// This function takes a vector of vectors `pks` as input and generates all possible permutations of the vectors in `pks`. + /// The function uses a modified version of the Heap's algorithm to generate the permutations. + /// It initializes an empty vector `perms` to store the permutations and a vector `c` to encode the stack state. + /// The function iterates through the vectors in `pks` and swaps elements based on the parity of the iteration index. + /// It outputs each new permutation and increments the stack state accordingly. + /// Finally, it returns the vector `perms` containing all the generated permutations. + /// + /// # Arguments + /// + /// * `pks` - A vector of vectors representing the input vectors to generate permutations for. + /// + /// # Returns + /// + /// A vector of vectors representing all possible permutations of the input vectors. + /// + /// # Examples + /// + /// ``` + /// let pks = vector[ + /// vector[1, 2, 3], + /// vector[4, 5], + /// vector[6, 7, 8, 9] + /// ]; + /// + /// let perms = permutations(pks); + /// + /// assert!(vector::length(&perms) == 6, 0); + /// assert!(*vector::borrow(&perms, 0) == vector[1, 2, 3], 0); + /// assert!(*vector::borrow(&perms, 1) == vector[4, 5], 0); + /// assert!(*vector::borrow(&perms, 2) == vector[6, 7, 8, 9], 0); + /// assert!(*vector::borrow(&perms, 3) == vector[2, 1, 3], 0); + /// assert!(*vector::borrow(&perms, 4) == vector[5, 4], 0); + /// assert!(*vector::borrow(&perms, 5) == vector[7, 6, 8, 9], 0); + /// ``` + public fun permutations( + pks: vector>, + ): vector>> { + // initialize an empty vector to store the permutations + let perms = vector::empty>>(); + // get the length of the pks vector + let n = vector::length(&pks); + // c is an encoding of the stack state. c[k] encodes the for-loop counter for when permutations(k - 1, pks) is called + let c = vector::empty(); + // initialize c with zeros + let i = 0; + while (i < n) { + vector::push_back(&mut c, 0); + i = i + 1 + }; + // output the first permutation + vector::push_back(&mut perms, pks); + // i acts similarly to a stack pointer + i = 1; + // loop until i is equal to n + while (i < n) { + // check if c[i] is less than i + if (*vector::borrow(&c, i) < i) { + // swap elements depending on the parity of i + if (i % 2 == 0) { + vector::swap(&mut pks, 0, i); + } else { + vector::swap(&mut pks, *vector::borrow(&c, (i as u64)), i); + }; + // output the new permutation + vector::push_back(&mut perms, pks); + // increment c[i] by 1 + *vector::borrow_mut(&mut c, i) = *vector::borrow(&c, i) + 1; + // reset i to 1 + i = 1; + } else { + // reset c[i] to 0, c[i] = 0; + *vector::borrow_mut(&mut c, i) = 0; + // increment i by 1 + i = i + 1; + }; + }; + // return the perms vector + perms + } + +} diff --git a/move/tests/multisig_tests.move b/move/tests/multisig_tests.move index 8c3f5dc..ab230cb 100644 --- a/move/tests/multisig_tests.move +++ b/move/tests/multisig_tests.move @@ -3,7 +3,6 @@ #[test_only] module multisig::multisig_unit_tests { - use std::vector; use multisig::multisig::{Self as ms}; #[test] @@ -32,24 +31,6 @@ module multisig::multisig_unit_tests { assert!(derived_multisig_address == expected_multisig_address, 0); } - #[test] - fun test_permutations() { - let ed25519_key: vector = vector[0, 13, 125, 171, 53, 140, 141, 173, 170, 78, 250, 0, 73, 167, 91, 7, 67, 101, 85, 177, 10, 54, 130, 25, 187, 104, 15, 112, 87, 19, 73, 215, 117]; - let secp256k1_key: vector = vector[1, 2, 14, 23, 205, 89, 57, 228, 107, 25, 102, 65, 150, 140, 215, 89, 145, 11, 162, 87, 126, 39, 250, 115, 253, 227, 135, 109, 185, 190, 197, 188, 235, 43]; - let secp256r1_key: vector = vector[2, 3, 71, 251, 175, 35, 240, 56, 171, 196, 195, 8, 162, 113, 17, 122, 42, 76, 255, 174, 221, 188, 95, 248, 28, 117, 23, 188, 108, 116, 167, 237, 180, 48]; - - let pks: vector> = vector[ed25519_key, secp256k1_key, secp256r1_key]; - let permutations: vector>> = ms::permutations(pks); - - assert!(vector::length(&permutations) == 6, 0); - assert!(*vector::borrow(&permutations, 0) == vector[ed25519_key, secp256k1_key, secp256r1_key], 0); - assert!(*vector::borrow(&permutations, 1) == vector[secp256k1_key, ed25519_key, secp256r1_key], 0); - assert!(*vector::borrow(&permutations, 2) == vector[secp256r1_key, ed25519_key, secp256k1_key], 0); - assert!(*vector::borrow(&permutations, 3) == vector[ed25519_key, secp256r1_key, secp256k1_key], 0); - assert!(*vector::borrow(&permutations, 4) == vector[secp256k1_key, secp256r1_key, ed25519_key], 0); - assert!(*vector::borrow(&permutations, 5) == vector[secp256r1_key, secp256k1_key, ed25519_key], 0); - } - #[test] fun test_order_pks() { let ed25519_key: vector = vector[0, 13, 125, 171, 53, 140, 141, 173, 170, 78, 250, 0, 73, 167, 91, 7, 67, 101, 85, 177, 10, 54, 130, 25, 187, 104, 15, 112, 87, 19, 73, 215, 117]; diff --git a/move/tests/utils_test.move b/move/tests/utils_test.move new file mode 100644 index 0000000..873543f --- /dev/null +++ b/move/tests/utils_test.move @@ -0,0 +1,26 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[test_only] +module multisig::utils_unit_tests { + use std::vector; + use multisig::utils; + + #[test] + fun test_permutations() { + let ed25519_key: vector = vector[0, 13, 125, 171, 53, 140, 141, 173, 170, 78, 250, 0, 73, 167, 91, 7, 67, 101, 85, 177, 10, 54, 130, 25, 187, 104, 15, 112, 87, 19, 73, 215, 117]; + let secp256k1_key: vector = vector[1, 2, 14, 23, 205, 89, 57, 228, 107, 25, 102, 65, 150, 140, 215, 89, 145, 11, 162, 87, 126, 39, 250, 115, 253, 227, 135, 109, 185, 190, 197, 188, 235, 43]; + let secp256r1_key: vector = vector[2, 3, 71, 251, 175, 35, 240, 56, 171, 196, 195, 8, 162, 113, 17, 122, 42, 76, 255, 174, 221, 188, 95, 248, 28, 117, 23, 188, 108, 116, 167, 237, 180, 48]; + + let pks: vector> = vector[ed25519_key, secp256k1_key, secp256r1_key]; + let permutations: vector>> = utils::permutations(pks); + + assert!(vector::length(&permutations) == 6, 0); + assert!(*vector::borrow(&permutations, 0) == vector[ed25519_key, secp256k1_key, secp256r1_key], 0); + assert!(*vector::borrow(&permutations, 1) == vector[secp256k1_key, ed25519_key, secp256r1_key], 0); + assert!(*vector::borrow(&permutations, 2) == vector[secp256r1_key, ed25519_key, secp256k1_key], 0); + assert!(*vector::borrow(&permutations, 3) == vector[ed25519_key, secp256r1_key, secp256k1_key], 0); + assert!(*vector::borrow(&permutations, 4) == vector[secp256k1_key, secp256r1_key, ed25519_key], 0); + assert!(*vector::borrow(&permutations, 5) == vector[secp256r1_key, secp256k1_key, ed25519_key], 0); + } +} \ No newline at end of file From b62da2ca6d11bf228ee91c704d3bd82964a8e835 Mon Sep 17 00:00:00 2001 From: George Digkas Date: Mon, 29 Jan 2024 10:48:58 +0200 Subject: [PATCH 4/5] minor refactorings --- move/sources/multisig.move | 2 +- move/sources/utils.move | 18 +++++++++--------- move/tests/utils_test.move | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/move/sources/multisig.move b/move/sources/multisig.move index 99235b3..1ac28e2 100644 --- a/move/sources/multisig.move +++ b/move/sources/multisig.move @@ -189,7 +189,7 @@ module multisig::multisig { threshold: u16, ): vector> { // loop through all the permutations of the pks vector - let perms = utils::permutations(pks); + let perms = utils::permutations(&mut pks); let n = vector::length(&perms); let i = 0; while (i < n) { diff --git a/move/sources/utils.move b/move/sources/utils.move index 401aa21..54196f9 100644 --- a/move/sources/utils.move +++ b/move/sources/utils.move @@ -41,13 +41,13 @@ module multisig::utils { /// assert!(*vector::borrow(&perms, 4) == vector[5, 4], 0); /// assert!(*vector::borrow(&perms, 5) == vector[7, 6, 8, 9], 0); /// ``` - public fun permutations( - pks: vector>, - ): vector>> { + public fun permutations( + pks: &mut vector, + ): vector> { // initialize an empty vector to store the permutations - let perms = vector::empty>>(); + let perms = vector::empty>(); // get the length of the pks vector - let n = vector::length(&pks); + let n = vector::length(pks); // c is an encoding of the stack state. c[k] encodes the for-loop counter for when permutations(k - 1, pks) is called let c = vector::empty(); // initialize c with zeros @@ -57,7 +57,7 @@ module multisig::utils { i = i + 1 }; // output the first permutation - vector::push_back(&mut perms, pks); + vector::push_back(&mut perms, *pks); // i acts similarly to a stack pointer i = 1; // loop until i is equal to n @@ -66,12 +66,12 @@ module multisig::utils { if (*vector::borrow(&c, i) < i) { // swap elements depending on the parity of i if (i % 2 == 0) { - vector::swap(&mut pks, 0, i); + vector::swap(pks, 0, i); } else { - vector::swap(&mut pks, *vector::borrow(&c, (i as u64)), i); + vector::swap(pks, *vector::borrow(&c, (i as u64)), i); }; // output the new permutation - vector::push_back(&mut perms, pks); + vector::push_back(&mut perms, *pks); // increment c[i] by 1 *vector::borrow_mut(&mut c, i) = *vector::borrow(&c, i) + 1; // reset i to 1 diff --git a/move/tests/utils_test.move b/move/tests/utils_test.move index 873543f..cd41ad1 100644 --- a/move/tests/utils_test.move +++ b/move/tests/utils_test.move @@ -13,7 +13,7 @@ module multisig::utils_unit_tests { let secp256r1_key: vector = vector[2, 3, 71, 251, 175, 35, 240, 56, 171, 196, 195, 8, 162, 113, 17, 122, 42, 76, 255, 174, 221, 188, 95, 248, 28, 117, 23, 188, 108, 116, 167, 237, 180, 48]; let pks: vector> = vector[ed25519_key, secp256k1_key, secp256r1_key]; - let permutations: vector>> = utils::permutations(pks); + let permutations: vector>> = utils::permutations(&mut pks); assert!(vector::length(&permutations) == 6, 0); assert!(*vector::borrow(&permutations, 0) == vector[ed25519_key, secp256k1_key, secp256r1_key], 0); From 9c3891161411115465c89633037fd3fa207db20a Mon Sep 17 00:00:00 2001 From: George Digkas Date: Wed, 31 Jan 2024 10:59:11 +0200 Subject: [PATCH 5/5] s/extected_ms_address/expected_ms_address --- move/sources/multisig.move | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/move/sources/multisig.move b/move/sources/multisig.move index 1ac28e2..6e9c183 100644 --- a/move/sources/multisig.move +++ b/move/sources/multisig.move @@ -172,7 +172,7 @@ module multisig::multisig { /// If no permutation matches the expected multisig address, it aborts with an error. /// /// Parameters: - /// - extected_ms_address: The expected multisig address to match. + /// - expected_ms_address: The expected multisig address to match. /// - pks: A vector of vectors containing the public keys. /// - weights: A vector of weights corresponding to the public keys. /// - threshold: The threshold value for multisig. @@ -183,7 +183,7 @@ module multisig::multisig { /// Abort: /// - ENoPermutationMatchesTheExpectedAddress: If no permutation matches the expected multisig address. public fun order_pks( - extected_ms_address: address, + expected_ms_address: address, pks: vector>, weights: vector, threshold: u16, @@ -196,7 +196,7 @@ module multisig::multisig { let perm: vector> = *vector::borrow(&perms, i); let ms_address = derive_multisig_address_quiet(perm, weights, threshold); // check if the ms_address matches the expected one - if (ms_address == extected_ms_address) { + if (ms_address == expected_ms_address) { return perm }; i = i + 1;