Merge 4e959a04a1
into 9620fca895
This commit is contained in:
commit
62f847a282
|
@ -103,7 +103,7 @@ contract Messages is Getters {
|
|||
|
||||
|
||||
/**
|
||||
* @dev verifySignatures serves to validate arbitrary sigatures against an arbitrary guardianSet
|
||||
* @dev verifySignatures serves to validate arbitrary signatures against an arbitrary guardianSet
|
||||
* - it intentionally does not solve for expectations within guardianSet (you should use verifyVM if you need these protections)
|
||||
* - it intentioanlly does not solve for quorum (you should use verifyVM if you need these protections)
|
||||
* - it intentionally returns true when signatures is an empty set (you should use verifyVM if you need these protections)
|
||||
|
@ -140,6 +140,44 @@ contract Messages is Getters {
|
|||
return (true, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev verifyCurrentQuorum serves to validate arbitrary signatures and check quorum against the current guardianSet
|
||||
*/
|
||||
function verifyCurrentQuorum(bytes32 hash, Structs.Signature[] memory signatures) public view returns (bool valid, string memory response) {
|
||||
uint32 gsi = getCurrentGuardianSetIndex();
|
||||
Structs.GuardianSet memory guardianSet = getGuardianSet(gsi);
|
||||
|
||||
/**
|
||||
* @dev Checks whether the guardianSet has zero keys
|
||||
* WARNING: This keys check is critical to ensure the guardianSet has keys present AND to ensure
|
||||
* that guardianSet key size doesn't fall to zero and negatively impact quorum assessment. If guardianSet
|
||||
* key length is 0 and vm.signatures length is 0, this could compromise the integrity of both vm and
|
||||
* signature verification.
|
||||
*/
|
||||
if(guardianSet.keys.length == 0){
|
||||
return (false, "invalid guardian set");
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev We're using a fixed point number transformation with 1 decimal to deal with rounding.
|
||||
* WARNING: This quorum check is critical to assessing whether we have enough Guardian signatures to validate a VM
|
||||
* if making any changes to this, obtain additional peer review. If guardianSet key length is 0 and
|
||||
* vm.signatures length is 0, this could compromise the integrity of both vm and signature verification.
|
||||
*/
|
||||
if (signatures.length < quorum(guardianSet.keys.length)){
|
||||
return (false, "no quorum");
|
||||
}
|
||||
|
||||
/// @dev Verify the proposed vm.signatures against the guardianSet
|
||||
(bool signaturesValid, string memory invalidReason) = verifySignatures(hash, signatures, guardianSet);
|
||||
if(!signaturesValid){
|
||||
return (false, invalidReason);
|
||||
}
|
||||
|
||||
/// If we are here, we've validated the VM is a valid multi-sig that matches the current guardianSet.
|
||||
return (true, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev parseVM serves to parse an encodedVM into a vm struct
|
||||
* - it intentionally performs no validation functions, it simply parses raw into a struct
|
||||
|
|
|
@ -92,6 +92,8 @@ interface IWormhole {
|
|||
|
||||
function verifySignatures(bytes32 hash, Signature[] memory signatures, GuardianSet memory guardianSet) external pure returns (bool valid, string memory reason);
|
||||
|
||||
function verifyCurrentQuorum(bytes32 hash, Signature[] memory signatures) external view returns (bool valid, string memory response);
|
||||
|
||||
function parseVM(bytes memory encodedVM) external pure returns (VM memory vm);
|
||||
|
||||
function quorum(uint numGuardians) external pure returns (uint numSignaturesRequiredForQuorum);
|
||||
|
|
|
@ -161,4 +161,137 @@ contract TestMessages is Test {
|
|||
assertEq(valid, false);
|
||||
assertEq(reason, "vm.hash doesn't match body");
|
||||
}
|
||||
|
||||
/// @dev This test checks that quorum is reached in the happy path
|
||||
function testFuzz_verifyCurrentQuorum(bytes memory encoded) public {
|
||||
vm.assume(encoded.length > 0);
|
||||
|
||||
// Set the initial guardian set
|
||||
address[] memory initialGuardians = new address[](1);
|
||||
initialGuardians[0] = testGuardianPub;
|
||||
|
||||
// Create a guardian set
|
||||
Structs.GuardianSet memory initialGuardianSet = Structs.GuardianSet({
|
||||
keys: initialGuardians,
|
||||
expirationTime: 0
|
||||
});
|
||||
|
||||
messages.storeGuardianSetPub(initialGuardianSet, uint32(0));
|
||||
|
||||
bytes32 message = keccak256(encoded);
|
||||
|
||||
// Generate legitimate signature.
|
||||
Structs.Signature memory goodSignature;
|
||||
(goodSignature.v, goodSignature.r, goodSignature.s) = vm.sign(testGuardian, message);
|
||||
assertEq(ecrecover(message, goodSignature.v, goodSignature.r, goodSignature.s), vm.addr(testGuardian));
|
||||
goodSignature.guardianIndex = 0;
|
||||
|
||||
// Attempt to verify signatures.
|
||||
Structs.Signature[] memory sigs = new Structs.Signature[](1);
|
||||
sigs[0] = goodSignature;
|
||||
|
||||
(bool valid, string memory reason) = messages.verifyCurrentQuorum(message, sigs);
|
||||
assertEq(valid, true);
|
||||
assertEq(bytes(reason).length, 0);
|
||||
}
|
||||
|
||||
/// @dev This test checks that quorum will not be reached if there are no guardians in the current guardian set
|
||||
function testFuzz_verifyCurrentQuorum_noGuardians(bytes memory encoded) public {
|
||||
vm.assume(encoded.length > 0);
|
||||
|
||||
bytes32 message = keccak256(encoded);
|
||||
|
||||
// Generate legitimate signature.
|
||||
Structs.Signature memory goodSignature;
|
||||
(goodSignature.v, goodSignature.r, goodSignature.s) = vm.sign(testGuardian, message);
|
||||
assertEq(ecrecover(message, goodSignature.v, goodSignature.r, goodSignature.s), vm.addr(testGuardian));
|
||||
goodSignature.guardianIndex = 0;
|
||||
|
||||
// Attempt to verify signatures.
|
||||
Structs.Signature[] memory sigs = new Structs.Signature[](1);
|
||||
sigs[0] = goodSignature;
|
||||
|
||||
(bool valid, string memory reason) = messages.verifyCurrentQuorum(message, sigs);
|
||||
assertEq(valid, false);
|
||||
assertEq(abi.encodePacked(reason), abi.encodePacked("invalid guardian set"));
|
||||
}
|
||||
|
||||
/// @dev This test checks that quorum will not be reached if there is >0 invalid signatures in any position
|
||||
function testFuzz_verifyCurrentQuorum_invalidSignature(bytes memory encoded, uint8 numSigs, uint256 invalidSigIndex) public {
|
||||
vm.assume(encoded.length > 0);
|
||||
vm.assume(numSigs != 0);
|
||||
invalidSigIndex = bound(invalidSigIndex, 0, numSigs - 1);
|
||||
|
||||
// Set the initial guardian set
|
||||
address[] memory initialGuardians = new address[](numSigs);
|
||||
|
||||
bytes32 message = keccak256(encoded);
|
||||
|
||||
Structs.Signature[] memory signatures = new Structs.Signature[](numSigs);
|
||||
|
||||
for (uint8 i = 0; i < numSigs; ++i) {
|
||||
(address addr, uint256 key) = makeAddrAndKey(string(abi.encode(i)));
|
||||
initialGuardians[i] = addr;
|
||||
|
||||
// Create a single invalid signature
|
||||
if (i == invalidSigIndex) {
|
||||
(addr, key) = makeAddrAndKey(string(abi.encode(type(uint64).max)));
|
||||
(signatures[i].v, signatures[i].r, signatures[i].s) = vm.sign(key, message);
|
||||
signatures[i].guardianIndex = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
(signatures[i].v, signatures[i].r, signatures[i].s) = vm.sign(key, message);
|
||||
signatures[i].guardianIndex = i;
|
||||
}
|
||||
|
||||
// Create a guardian set
|
||||
Structs.GuardianSet memory initialGuardianSet = Structs.GuardianSet({
|
||||
keys: initialGuardians,
|
||||
expirationTime: 0
|
||||
});
|
||||
|
||||
messages.storeGuardianSetPub(initialGuardianSet, uint32(0));
|
||||
|
||||
(bool valid, string memory reason) = messages.verifyCurrentQuorum(message, signatures);
|
||||
assertEq(valid, false);
|
||||
assertEq(abi.encodePacked(reason), abi.encodePacked("VM signature invalid"));
|
||||
}
|
||||
|
||||
/// @dev This test checks that quorum will not be reached with <quorum valid signatures
|
||||
function testFuzz_verifyCurrentQuorum_noQuorum(bytes memory encoded, uint256 numGuardians, uint256 numSigs) public {
|
||||
vm.assume(encoded.length > 0);
|
||||
numGuardians = bound(numGuardians, 1, type(uint8).max);
|
||||
numSigs = bound(numSigs, 0, messages.quorum(numGuardians) - 1);
|
||||
|
||||
// Set the initial guardian set
|
||||
address[] memory initialGuardians = new address[](numGuardians);
|
||||
|
||||
bytes32 message = keccak256(encoded);
|
||||
|
||||
Structs.Signature[] memory signatures = new Structs.Signature[](numSigs);
|
||||
|
||||
for (uint8 i = 0; i < numGuardians; ++i) {
|
||||
(address addr, uint256 key) = makeAddrAndKey(string(abi.encode(i)));
|
||||
initialGuardians[i] = addr;
|
||||
|
||||
if (i < numSigs) {
|
||||
(signatures[i].v, signatures[i].r, signatures[i].s) = vm.sign(key, message);
|
||||
signatures[i].guardianIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a guardian set
|
||||
Structs.GuardianSet memory initialGuardianSet = Structs.GuardianSet({
|
||||
keys: initialGuardians,
|
||||
expirationTime: 0
|
||||
});
|
||||
|
||||
messages.storeGuardianSetPub(initialGuardianSet, uint32(0));
|
||||
|
||||
(bool valid, string memory reason) = messages.verifyCurrentQuorum(message, signatures);
|
||||
assertEq(valid, false);
|
||||
assertEq(abi.encodePacked(reason), abi.encodePacked("no quorum"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -223,6 +223,13 @@ contract MockWormhole is IWormhole {
|
|||
revert("unsupported verifySignatures in wormhole mock");
|
||||
}
|
||||
|
||||
function verifyCurrentQuorum(
|
||||
bytes32, /*hash*/
|
||||
Signature[] memory /*signatures*/
|
||||
) external pure returns (bool /*valid*/, string memory /*reason*/) {
|
||||
revert("unsupported verifyCurrentQuorum in wormhole mock");
|
||||
}
|
||||
|
||||
function parseContractUpgrade(bytes memory /*encodedUpgrade*/ )
|
||||
external
|
||||
pure
|
||||
|
|
Loading…
Reference in New Issue