Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(auth): extract validation to weight signers and proof #140

Merged
merged 58 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
aca7b85
chore: init refactor validate_signatures
npty Sep 11, 2024
81abd32
refactor: find! macro
npty Sep 11, 2024
e7662e4
chore: handle get signer weight
npty Sep 11, 2024
2ea7d90
chore: fix compile error
npty Sep 12, 2024
b7227e1
chore: refactor validate_signatures
npty Sep 12, 2024
223a44c
chore: change from abort to assert
npty Sep 12, 2024
eaebf05
chore: refactor validate_signers
npty Sep 12, 2024
ac042aa
chore: move find! macro to new section
npty Sep 12, 2024
ea06f61
chore: extract validate_signer
npty Sep 12, 2024
1115fea
chore: move validate function to weight_signer
npty Sep 12, 2024
628da67
chore: move functions to weight_signer(s)
npty Sep 12, 2024
bd8a84e
chore: remove macro in auth
npty Sep 12, 2024
ab4d199
chore: move validate_signer to proof module
npty Sep 12, 2024
3fbb825
chore: revert compiler version
npty Sep 12, 2024
40f3936
chore: update error code
npty Sep 12, 2024
2296670
chore: remove internal function section
npty Sep 12, 2024
878f8a1
chore: remove extra line
npty Sep 12, 2024
4302d6d
chore: remove extra line in weighted_signers
npty Sep 12, 2024
6c862f3
Merge branch 'main' into refactor/auth
npty Sep 12, 2024
d42782a
chore: use method syntax
npty Sep 12, 2024
648a862
Merge branch 'refactor/auth' of github.com:axelarnetwork/axelar-cgp-s…
npty Sep 12, 2024
29fbd2c
chore: reorder
npty Sep 12, 2024
c9a71be
chore: add comments
npty Sep 12, 2024
0f2cff8
refactor: weighted_signer to reuse weight validation
npty Sep 12, 2024
ecb74c5
refactor: weight_signers::validate
npty Sep 12, 2024
0c0e65b
chore: fix format error
npty Sep 12, 2024
f7f1aa0
chore: add comments
npty Sep 12, 2024
71cb43d
chore: remove type infer
npty Sep 12, 2024
531bb68
chore: remove curly brace around single import
npty Sep 12, 2024
48e2226
chore: change `EInvalidOperators` to `EInvalidSigners`
npty Sep 12, 2024
a7ef161
chore: fix signers access
npty Sep 12, 2024
10ca8f0
chore: remove type infer for weight_signers
npty Sep 12, 2024
d475f34
chore: use index to find signer
npty Sep 13, 2024
26b4d0d
chore: cleanup weighted_signers::validate
npty Sep 13, 2024
35ce313
fix: weighted_signers::validate
npty Sep 13, 2024
6d4cfdd
refactor: parse_weight function
npty Sep 13, 2024
6ccb4a6
refactor: move MessageToSign to proof module
npty Sep 13, 2024
7a4f7bb
chore: revert back to while loop
npty Sep 13, 2024
b36ab0f
chore: remove find! macro and move find_signer_weight
npty Sep 13, 2024
cc1d467
chore: EInvalidWeights to EInvalidWeight
npty Sep 13, 2024
19c1093
chore: fix a copy of signers
npty Sep 13, 2024
2d0c8dd
chore: fix a copy of threshold
npty Sep 13, 2024
6bdb5a1
chore: do -> do_ref
npty Sep 13, 2024
bc342c3
chore: remove parse_weight
npty Sep 13, 2024
222ae63
chore: move MessageToSign back
npty Sep 13, 2024
e406dfb
Merge branch 'main' into refactor/auth
npty Sep 13, 2024
91724e6
chore: avoid copy in validate function
npty Sep 15, 2024
0f82844
chore: remove validate_weight
npty Sep 15, 2024
c06e921
chore: update error comment
npty Sep 15, 2024
1372258
chore: update peel function doc
npty Sep 15, 2024
e6ab45e
chore: add `EInvalidSignerOrder` error
npty Sep 15, 2024
823045b
chore: avoid making a copy
npty Sep 15, 2024
9410e36
chore: make find_weight_by_pub_key_from internal
npty Sep 15, 2024
554f881
chore: weight_signers -> signers
npty Sep 15, 2024
f9c122f
chore: returns a reference in signers function
npty Sep 15, 2024
37861f3
chore: update assert to abort
npty Sep 15, 2024
d23da3f
chore: remove unnecessary assert
npty Sep 16, 2024
ddc955a
Merge branch 'main' into refactor/auth
milapsheth Sep 17, 2024
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
5 changes: 5 additions & 0 deletions .changeset/fast-numbers-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@axelar-network/axelar-cgp-sui': patch
---

refactor: auth module
2 changes: 1 addition & 1 deletion move/axelar_gateway/Move.lock
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ dependencies = [
]

[move.toolchain-version]
compiler-version = "1.32.2"
compiler-version = "1.32.0"
edition = "2024.beta"
flavor = "sui"
88 changes: 6 additions & 82 deletions move/axelar_gateway/sources/auth.move
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
module axelar_gateway::auth;

use axelar_gateway::bytes32::{Self, Bytes32};
use axelar_gateway::proof::{Proof, Signature};
use axelar_gateway::weighted_signer;
use axelar_gateway::proof::{Proof};
npty marked this conversation as resolved.
Show resolved Hide resolved
use axelar_gateway::weighted_signers::WeightedSigners;
use sui::bcs;
npty marked this conversation as resolved.
Show resolved Hide resolved
use sui::clock::Clock;
Expand All @@ -12,15 +11,9 @@ use sui::table::{Self, Table};
// ------
// Errors
// ------
const EInvalidWeights: u64 = 0;
const EInvalidThreshold: u64 = 1;
/// For when operators have changed, and proof is no longer valid.
const EInvalidOperators: u64 = 2;
const EInsufficientRotationDelay: u64 = 3;
const EInsufficientRotationDelay: u64 = 0;
/// For when number of signatures for the call approvals is below the threshold.
const ELowSignaturesWeight: u64 = 4;
const EMalformedSigners: u64 = 5;
const EInvalidEpoch: u64 = 6;
const EInvalidEpoch: u64 = 1;

// -----
// Types
Expand Down Expand Up @@ -109,17 +102,11 @@ public(package) fun validate_proof(
EInvalidEpoch,
);

let message = MessageToSign {
proof.validate(bcs::to_bytes(&MessageToSign {
domain_separator: self.domain_separator,
signers_hash,
data_hash,
};

validate_signatures(
bcs::to_bytes(&message),
signers,
proof.signatures(),
);
}));

is_latest_signers
}
Expand All @@ -130,7 +117,7 @@ public(package) fun rotate_signers(
new_signers: WeightedSigners,
enforce_rotation_delay: bool,
) {
validate_signers(&new_signers);
new_signers.validate();

self.update_rotation_timestamp(clock, enforce_rotation_delay);

Expand All @@ -152,69 +139,6 @@ public(package) fun rotate_signers(
// Internal Functions
// ------------------

fun validate_signatures(
message: vector<u8>,
signers: &WeightedSigners,
signatures: &vector<Signature>,
) {
let signers_length = signers.signers().length();
let signatures_length = signatures.length();
assert!(signatures_length != 0, ELowSignaturesWeight);

let threshold = signers.threshold();
let mut signer_index = 0;
let mut total_weight = 0;
let mut i = 0;

while (i < signatures_length) {
let pub_key = signatures[i].recover_pub_key(&message);

while (
signer_index < signers_length &&
signers.signers()[signer_index].pub_key() != pub_key
) {
signer_index = signer_index + 1;
};

assert!(signer_index < signers_length, EMalformedSigners);

total_weight = total_weight + signers.signers()[signer_index].weight();

if (total_weight >= threshold) {
return
};

signer_index = signer_index + 1;
i = i + 1;
};

abort ELowSignaturesWeight
}

fun validate_signers(signers: &WeightedSigners) {
let signers_length = signers.signers().length();
assert!(signers_length != 0, EInvalidOperators);

let mut total_weight = 0;
let mut i = 0;
let mut previous_signer = weighted_signer::default();

while (i < signers_length) {
let current_signer = signers.signers()[i];
assert!(previous_signer.lt(&current_signer), EInvalidOperators);

let weight = current_signer.weight();
assert!(weight != 0, EInvalidWeights);

total_weight = total_weight + weight;
i = i + 1;
previous_signer = current_signer;
};

let threshold = signers.threshold();
assert!(threshold != 0 && total_weight >= threshold, EInvalidThreshold);
}

fun update_rotation_timestamp(
self: &mut AxelarSigners,
clock: &Clock,
Expand Down
57 changes: 57 additions & 0 deletions move/axelar_gateway/sources/types/proof.move
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const SIGNATURE_LENGTH: u64 = 65;
// ------
/// Invalid length of the bytes
const EInvalidLength: u64 = 0;
const ELowSignaturesWeight: u64 = 1;
const EMalformedSigners: u64 = 2;

// ----------------
// Public Functions
Expand Down Expand Up @@ -60,6 +62,61 @@ public(package) fun recover_pub_key(
ecdsa::secp256k1_ecrecover(&self.bytes, message, 0)
}

/// Validates the signatures of a message against the signers.
/// The total weight of the signatures must be greater than or equal to the threshold.
/// Otherwise, the error `ELowSignaturesWeight` is raised.
public(package) fun validate(self: &Proof, message: vector<u8>) {
let signers = &self.signers;
let signatures = &self.signatures;
assert!(signatures.length() != 0, ELowSignaturesWeight);

let threshold = signers.threshold();
let signatures_length = signatures.length();
let mut total_weight: u128 = 0;
let mut signer_index = 0;
let mut i = 0;

while (i < signatures_length) {
let pub_key = signatures[i].recover_pub_key(&message);

let (weight, index) = find_weight_by_pub_key_from(
signers,
signer_index,
&pub_key,
);

total_weight = total_weight + weight;

if (total_weight >= threshold) return;

i = i + 1;
signer_index = index + 1;
};

abort ELowSignaturesWeight
}

/// Finds the weight of a signer in the weighted signers by its public key.
fun find_weight_by_pub_key_from(
signers: &WeightedSigners,
signer_index: u64,
pub_key: &vector<u8>,
): (u128, u64) {
let signers = signers.signers();
let length = signers.length();
let mut index = signer_index;

// Find the first signer that satisfies the predicate
while (index < length && signers[index].pub_key() != pub_key) {
index = index + 1;
};

// If no signer satisfies the predicate, return an error
assert!(index < length, EMalformedSigners);

(signers[index].weight(), index)
}

public(package) fun peel_signature(bcs: &mut BCS): Signature {
let bytes = bcs.peel_vec_u8();

Expand Down
5 changes: 5 additions & 0 deletions move/axelar_gateway/sources/types/weighted_signer.move
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public fun weight(self: &WeightedSigner): u128 {
// ------

const EInvalidPubKeyLength: u64 = 0;
const EInvalidWeight: u64 = 1;

// -----------------
// Package Functions
Expand Down Expand Up @@ -60,6 +61,10 @@ public(package) fun peel(bcs: &mut BCS): WeightedSigner {
new(pub_key, weight)
}

public(package) fun validate(self: &WeightedSigner) {
assert!(self.weight != 0, EInvalidWeight);
}

/// Check if self.signer is less than other.signer as bytes
public(package) fun lt(self: &WeightedSigner, other: &WeightedSigner): bool {
let mut i = 0;
Expand Down
55 changes: 52 additions & 3 deletions move/axelar_gateway/sources/types/weighted_signers.move
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,42 @@ public struct WeightedSigners has copy, drop, store {
/// ------
/// Invalid length of the bytes
const EInvalidLength: u64 = 0;
const EInvalidThreshold: u64 = 1;
/// Invalid signer weights or threshold
const EInvalidSigners: u64 = 2;
const EInvalidSignerOrder: u64 = 3;

/// -----------------
/// Package Functions
/// -----------------

/// Decode a `WeightedSigners` from the BCS encoded bytes.
public(package) fun peel(bcs: &mut BCS): WeightedSigners {
let len = bcs.peel_vec_length();
assert!(len > 0, EInvalidLength);

WeightedSigners {
signers: vector::tabulate!(len, |_| weighted_signer::peel(bcs)),
threshold: bcs.peel_u128(),
nonce: bytes32::peel(bcs)
nonce: bytes32::peel(bcs),
}
}

/// Validates the weighted signers. The following must be true:
/// 1. The signers are in ascending order by their public key.
/// 2. The threshold is greater than zero.
/// 3. The threshold is less than or equal to the total weight of the signers.
public(package) fun validate(self: &WeightedSigners) {
self.validate_signers();
self.validate_threshold();
}

public(package) fun hash(self: &WeightedSigners): Bytes32 {
bytes32::from_bytes(hash::keccak256(&bcs::to_bytes(self)))
}

public(package) fun signers(self: &WeightedSigners): vector<WeightedSigner> {
self.signers
public(package) fun signers(self: &WeightedSigners): &vector<WeightedSigner> {
&self.signers
}

public(package) fun threshold(self: &WeightedSigners): u128 {
Expand All @@ -47,6 +62,40 @@ public(package) fun nonce(self: &WeightedSigners): Bytes32 {
self.nonce
}

/// -----
/// Internal Functions
/// -----

/// Validates the order of the signers and the length of the signers.
/// The signers must be in ascending order by their public key.
/// Otherwise, the error `EInvalidSigners` is raised.
fun validate_signers(self: &WeightedSigners) {
assert!(!self.signers.is_empty(), EInvalidSigners);
let mut previous = &weighted_signer::default();
self.signers.do_ref!(
|signer| {
signer.validate();
assert!(previous.lt(signer), EInvalidSignerOrder);
previous = signer;
},
);
}

/// Calculates the total weight of the signers.
fun total_weight(self: &WeightedSigners): u128 {
self.signers.fold!<WeightedSigner, u128>(
0,
|acc, signer| acc + signer.weight(),
)
}

/// Validates the threshold.
/// The threshold must be greater than zero and less than or equal to the total weight of the signers.
/// Otherwise, the error `EInvalidThreshold` is raised.
fun validate_threshold(self: &WeightedSigners) {
assert!(self.threshold != 0 && self.total_weight() >= self.threshold, EInvalidThreshold);
}

#[test_only]
public fun create_for_testing(
signers: vector<WeightedSigner>,
Expand Down
Loading