141 lines
4.0 KiB
Solidity
141 lines
4.0 KiB
Solidity
// contracts/Messages.sol
|
|
// SPDX-License-Identifier: Apache 2
|
|
|
|
pragma solidity ^0.8.0;
|
|
pragma experimental ABIEncoderV2;
|
|
|
|
import "./Getters.sol";
|
|
import "./Structs.sol";
|
|
import "../libraries/external/BytesLib.sol";
|
|
|
|
contract Messages is Getters {
|
|
using BytesLib for bytes;
|
|
|
|
function parseAndVerifyVM(
|
|
bytes calldata encodedVM
|
|
)
|
|
public
|
|
view
|
|
returns (Structs.VM memory vm, bool valid, string memory reason)
|
|
{
|
|
vm = parseVM(encodedVM);
|
|
(valid, reason) = verifyVM(vm);
|
|
}
|
|
|
|
function verifyVM(
|
|
Structs.VM memory vm
|
|
) public view returns (bool valid, string memory reason) {
|
|
Structs.GuardianSet memory guardianSet = getGuardianSet(
|
|
vm.guardianSetIndex
|
|
);
|
|
|
|
if (guardianSet.keys.length == 0) {
|
|
return (false, "invalid guardian set");
|
|
}
|
|
if (
|
|
vm.guardianSetIndex != getCurrentGuardianSetIndex() &&
|
|
guardianSet.expirationTime < block.timestamp
|
|
) {
|
|
return (false, "guardian set has expired");
|
|
}
|
|
// We're using a fixed point number transformation with 1 decimal to deal with rounding.
|
|
if (
|
|
(((guardianSet.keys.length * 10) / 3) * 2) / 10 + 1 >
|
|
vm.signatures.length
|
|
) {
|
|
return (false, "no quorum");
|
|
}
|
|
|
|
// Verify signatures
|
|
(bool signaturesValid, string memory invalidReason) = verifySignatures(
|
|
vm.hash,
|
|
vm.signatures,
|
|
guardianSet
|
|
);
|
|
if (!signaturesValid) {
|
|
return (false, invalidReason);
|
|
}
|
|
|
|
return (true, "");
|
|
}
|
|
|
|
function verifySignatures(
|
|
bytes32 hash,
|
|
Structs.Signature[] memory signatures,
|
|
Structs.GuardianSet memory guardianSet
|
|
) public pure returns (bool valid, string memory reason) {
|
|
uint8 lastIndex = 0;
|
|
for (uint i = 0; i < signatures.length; i++) {
|
|
Structs.Signature memory sig = signatures[i];
|
|
|
|
require(
|
|
i == 0 || sig.guardianIndex > lastIndex,
|
|
"signature indices must be ascending"
|
|
);
|
|
lastIndex = sig.guardianIndex;
|
|
|
|
if (
|
|
ecrecover(hash, sig.v, sig.r, sig.s) !=
|
|
guardianSet.keys[sig.guardianIndex]
|
|
) {
|
|
return (false, "VM signature invalid");
|
|
}
|
|
}
|
|
return (true, "");
|
|
}
|
|
|
|
function parseVM(
|
|
bytes memory encodedVM
|
|
) public pure virtual returns (Structs.VM memory vm) {
|
|
uint index = 0;
|
|
|
|
vm.version = encodedVM.toUint8(index);
|
|
index += 1;
|
|
require(vm.version == 1, "VM version incompatible");
|
|
|
|
vm.guardianSetIndex = encodedVM.toUint32(index);
|
|
index += 4;
|
|
|
|
// Parse Signatures
|
|
uint256 signersLen = encodedVM.toUint8(index);
|
|
index += 1;
|
|
vm.signatures = new Structs.Signature[](signersLen);
|
|
for (uint i = 0; i < signersLen; i++) {
|
|
vm.signatures[i].guardianIndex = encodedVM.toUint8(index);
|
|
index += 1;
|
|
|
|
vm.signatures[i].r = encodedVM.toBytes32(index);
|
|
index += 32;
|
|
vm.signatures[i].s = encodedVM.toBytes32(index);
|
|
index += 32;
|
|
vm.signatures[i].v = encodedVM.toUint8(index) + 27;
|
|
index += 1;
|
|
}
|
|
|
|
// Hash the body
|
|
bytes memory body = encodedVM.slice(index, encodedVM.length - index);
|
|
vm.hash = keccak256(abi.encodePacked(keccak256(body)));
|
|
|
|
// Parse the body
|
|
vm.timestamp = encodedVM.toUint32(index);
|
|
index += 4;
|
|
|
|
vm.nonce = encodedVM.toUint32(index);
|
|
index += 4;
|
|
|
|
vm.emitterChainId = encodedVM.toUint16(index);
|
|
index += 2;
|
|
|
|
vm.emitterAddress = encodedVM.toBytes32(index);
|
|
index += 32;
|
|
|
|
vm.sequence = encodedVM.toUint64(index);
|
|
index += 8;
|
|
|
|
vm.consistencyLevel = encodedVM.toUint8(index);
|
|
index += 1;
|
|
|
|
vm.payload = encodedVM.slice(index, encodedVM.length - index);
|
|
}
|
|
}
|