Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions move/sources/multisig.move
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ 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;

/// 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.
struct MultisigAddressEvent has copy, drop {
pks: vector<vector<u8>>,
Expand Down Expand Up @@ -162,4 +166,43 @@ 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:
/// - 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.
///
/// 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(
expected_ms_address: address,
pks: vector<vector<u8>>,
weights: vector<u8>,
threshold: u16,
): vector<vector<u8>> {
// loop through all the permutations of the pks vector
let perms = utils::permutations(&mut pks);
let n = vector::length(&perms);
let i = 0;
while (i < n) {
let perm: vector<vector<u8>> = *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 == expected_ms_address) {
return perm
};
i = i + 1;
};

abort ENoPermutationMatchesTheExpectedAddress
}

}
90 changes: 90 additions & 0 deletions move/sources/utils.move
Original file line number Diff line number Diff line change
@@ -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<T: copy>(
pks: &mut vector<T>,
): vector<vector<T>> {
// initialize an empty vector to store the permutations
let perms = vector::empty<vector<T>>();
// 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<u64>();
// 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(pks, 0, i);
} else {
vector::swap(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
}

}
71 changes: 61 additions & 10 deletions move/tests/multisig_tests.move
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ module multisig::multisig_unit_tests {
#[test]
fun test_derive_multisig_address() {
// ED25519
let address_ED25519: address = @0x73a6b3c33e2d63383de5c6786cbaca231ff789f4c853af6d54cb883d8780adc0;
let key_ED25519: vector<u8> = 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<u8> = 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<u8> = 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<u8> = 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<u8> = 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<u8> = 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<u8>> = vector[key_ED25519, key_Secp256k1, key_Secp256r1];
let pks: vector<vector<u8>> = vector[ed25519_key, secp256k1_key, secp256r1_key];
let weights: vector<u8> = vector[1, 1, 1];
let threshold: u16 = 2;

Expand All @@ -31,4 +31,55 @@ module multisig::multisig_unit_tests {
assert!(derived_multisig_address == expected_multisig_address, 0);
}

#[test]
fun test_order_pks() {
let ed25519_key: vector<u8> = 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<u8> = 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<u8> = 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<u8>> = vector[ed25519_key, secp256k1_key, secp256r1_key];
let pks1: vector<vector<u8>> = vector[secp256k1_key, ed25519_key, secp256r1_key];
let pks2: vector<vector<u8>> = vector[secp256r1_key, ed25519_key, secp256k1_key];
let pks3: vector<vector<u8>> = vector[ed25519_key, secp256r1_key, secp256k1_key];
let pks4: vector<vector<u8>> = vector[secp256k1_key, secp256r1_key, ed25519_key];
let pks5: vector<vector<u8>> = vector[secp256r1_key, secp256k1_key, ed25519_key];

let ordered_pks: vector<vector<u8>> = vector[ed25519_key, secp256k1_key, secp256r1_key];
let weights: vector<u8> = 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<u8> = 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<u8> = 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<u8> = 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<u8>> = vector[ed25519_key, secp256k1_key, secp256r1_key];
let pks1: vector<vector<u8>> = vector[secp256k1_key, ed25519_key, secp256r1_key];
let pks2: vector<vector<u8>> = vector[secp256r1_key, ed25519_key, secp256k1_key];
let pks3: vector<vector<u8>> = vector[ed25519_key, secp256r1_key, secp256k1_key];
let pks4: vector<vector<u8>> = vector[secp256k1_key, secp256r1_key, ed25519_key];
let pks5: vector<vector<u8>> = vector[secp256r1_key, secp256k1_key, ed25519_key];

let weights: vector<u8> = 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);
}
}
26 changes: 26 additions & 0 deletions move/tests/utils_test.move
Original file line number Diff line number Diff line change
@@ -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<u8> = 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<u8> = 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<u8> = 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<u8>> = vector[ed25519_key, secp256k1_key, secp256r1_key];
let permutations: vector<vector<vector<u8>>> = utils::permutations(&mut 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);
}
}