-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathAxelarAuthWeighted.sol
124 lines (99 loc) · 4.53 KB
/
AxelarAuthWeighted.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
import { IAxelarAuthWeighted } from '../interfaces/IAxelarAuthWeighted.sol';
import { ECDSA } from '../ECDSA.sol';
import { Ownable } from '../Ownable.sol';
contract AxelarAuthWeighted is Ownable, IAxelarAuthWeighted {
uint256 public currentEpoch;
mapping(uint256 => bytes32) public hashForEpoch;
mapping(bytes32 => uint256) public epochForHash;
uint8 internal constant OLD_KEY_RETENTION = 16;
constructor(bytes[] memory recentOperators) {
for (uint256 i; i < recentOperators.length; ++i) {
_transferOperatorship(recentOperators[i]);
}
}
/**************************\
|* External Functionality *|
\**************************/
function validateProof(bytes32 messageHash, bytes calldata proof) external view returns (bool currentOperators) {
(address[] memory operators, uint256[] memory weights, uint256 threshold, bytes[] memory signatures) = abi.decode(
proof,
(address[], uint256[], uint256, bytes[])
);
bytes32 operatorsHash = keccak256(abi.encode(operators, weights, threshold));
uint256 operatorsEpoch = epochForHash[operatorsHash];
uint256 epoch = currentEpoch;
if (operatorsEpoch == 0 || epoch - operatorsEpoch >= OLD_KEY_RETENTION) revert InvalidOperators();
_validateSignatures(messageHash, operators, weights, threshold, signatures);
currentOperators = operatorsEpoch == epoch;
}
/***********************\
|* Owner Functionality *|
\***********************/
function transferOperatorship(bytes calldata params) external onlyOwner {
_transferOperatorship(params);
}
/**************************\
|* Internal Functionality *|
\**************************/
function _transferOperatorship(bytes memory params) internal {
(address[] memory newOperators, uint256[] memory newWeights, uint256 newThreshold) = abi.decode(
params,
(address[], uint256[], uint256)
);
uint256 operatorsLength = newOperators.length;
uint256 weightsLength = newWeights.length;
// operators must be sorted binary or alphabetically in lower case
if (operatorsLength == 0 || !_isSortedAscAndContainsNoDuplicate(newOperators)) revert InvalidOperators();
if (weightsLength != operatorsLength) revert InvalidWeights();
uint256 totalWeight = 0;
for (uint256 i = 0; i < weightsLength; ++i) {
totalWeight += newWeights[i];
}
if (newThreshold == 0 || totalWeight < newThreshold) revert InvalidThreshold();
bytes32 newOperatorsHash = keccak256(params);
if (epochForHash[newOperatorsHash] > 0) revert SameOperators();
uint256 epoch = currentEpoch + 1;
currentEpoch = epoch;
hashForEpoch[epoch] = newOperatorsHash;
epochForHash[newOperatorsHash] = epoch;
emit OperatorshipTransferred(newOperators, newWeights, newThreshold);
}
function _validateSignatures(
bytes32 messageHash,
address[] memory operators,
uint256[] memory weights,
uint256 threshold,
bytes[] memory signatures
) internal pure {
uint256 operatorsLength = operators.length;
uint256 operatorIndex = 0;
uint256 weight = 0;
// looking for signers within operators
// assuming that both operators and signatures are sorted
for (uint256 i = 0; i < signatures.length; ++i) {
address signer = ECDSA.recover(messageHash, signatures[i]);
// looping through remaining operators to find a match
for (; operatorIndex < operatorsLength && signer != operators[operatorIndex]; ++operatorIndex) {}
// checking if we are out of operators
if (operatorIndex == operatorsLength) revert MalformedSigners();
// return if weight sum above threshold
weight += weights[operatorIndex];
// weight needs to reach or surpass threshold
if (weight >= threshold) return;
// increasing operators index if match was found
++operatorIndex;
}
// if weight sum below threshold
revert MalformedSigners();
}
function _isSortedAscAndContainsNoDuplicate(address[] memory accounts) internal pure returns (bool) {
for (uint256 i; i < accounts.length - 1; ++i) {
if (accounts[i] >= accounts[i + 1]) {
return false;
}
}
return accounts[0] != address(0);
}
}