Messages.sol 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. // contracts/Messages.sol
  2. // SPDX-License-Identifier: Apache 2
  3. pragma solidity ^0.8.0;
  4. pragma experimental ABIEncoderV2;
  5. import "./Getters.sol";
  6. import "./Structs.sol";
  7. import "./libraries/external/BytesLib.sol";
  8. contract Messages is Getters {
  9. using BytesLib for bytes;
  10. function parseAndVerifyVM(bytes calldata encodedVM) public view returns (Structs.VM memory vm, bool valid, string memory reason) {
  11. vm = parseVM(encodedVM);
  12. (valid, reason) = verifyVM(vm);
  13. }
  14. function verifyVM(Structs.VM memory vm) public view returns (bool valid, string memory reason) {
  15. Structs.GuardianSet memory guardianSet = getGuardianSet(vm.guardianSetIndex);
  16. if(guardianSet.keys.length == 0){
  17. return (false, "invalid guardian set");
  18. }
  19. if(vm.guardianSetIndex != getCurrentGuardianSetIndex() && guardianSet.expirationTime < block.timestamp){
  20. return (false, "guardian set has expired");
  21. }
  22. // We're using a fixed point number transformation with 1 decimal to deal with rounding.
  23. if(((guardianSet.keys.length * 10 / 3) * 2) / 10 + 1 > vm.signatures.length){
  24. return (false, "no quorum");
  25. }
  26. // Verify signatures
  27. (bool signaturesValid, string memory invalidReason) = verifySignatures(vm.hash, vm.signatures, guardianSet);
  28. if(!signaturesValid){
  29. return (false, invalidReason);
  30. }
  31. return (true, "");
  32. }
  33. function verifySignatures(bytes32 hash, Structs.Signature[] memory signatures, Structs.GuardianSet memory guardianSet) public pure returns (bool valid, string memory reason) {
  34. uint8 lastIndex = 0;
  35. for (uint i = 0; i < signatures.length; i++) {
  36. Structs.Signature memory sig = signatures[i];
  37. require(i == 0 || sig.guardianIndex > lastIndex, "signature indices must be ascending");
  38. lastIndex = sig.guardianIndex;
  39. if(ecrecover(hash, sig.v, sig.r, sig.s) != guardianSet.keys[sig.guardianIndex]){
  40. return (false, "VM signature invalid");
  41. }
  42. }
  43. return (true, "");
  44. }
  45. function parseVM(bytes memory encodedVM) public pure virtual returns (Structs.VM memory vm) {
  46. uint index = 0;
  47. vm.version = encodedVM.toUint8(index);
  48. index += 1;
  49. require(vm.version == 1, "VM version incompatible");
  50. vm.guardianSetIndex = encodedVM.toUint32(index);
  51. index += 4;
  52. // Parse Signatures
  53. uint256 signersLen = encodedVM.toUint8(index);
  54. index += 1;
  55. vm.signatures = new Structs.Signature[](signersLen);
  56. for (uint i = 0; i < signersLen; i++) {
  57. vm.signatures[i].guardianIndex = encodedVM.toUint8(index);
  58. index += 1;
  59. vm.signatures[i].r = encodedVM.toBytes32(index);
  60. index += 32;
  61. vm.signatures[i].s = encodedVM.toBytes32(index);
  62. index += 32;
  63. vm.signatures[i].v = encodedVM.toUint8(index) + 27;
  64. index += 1;
  65. }
  66. // Hash the body
  67. bytes memory body = encodedVM.slice(index, encodedVM.length - index);
  68. vm.hash = keccak256(abi.encodePacked(keccak256(body)));
  69. // Parse the body
  70. vm.timestamp = encodedVM.toUint32(index);
  71. index += 4;
  72. vm.nonce = encodedVM.toUint32(index);
  73. index += 4;
  74. vm.emitterChainId = encodedVM.toUint16(index);
  75. index += 2;
  76. vm.emitterAddress = encodedVM.toBytes32(index);
  77. index += 32;
  78. vm.sequence = encodedVM.toUint64(index);
  79. index += 8;
  80. vm.consistencyLevel = encodedVM.toUint8(index);
  81. index += 1;
  82. vm.payload = encodedVM.slice(index, encodedVM.length - index);
  83. }
  84. }