2020-07-26 09:04:45 -07:00
|
|
|
// contracts/Wormhole.sol
|
2020-08-03 06:09:40 -07:00
|
|
|
// SPDX-License-Identifier: Apache 2
|
2020-07-26 09:04:45 -07:00
|
|
|
|
|
|
|
pragma solidity ^0.6.0;
|
|
|
|
pragma experimental ABIEncoderV2;
|
|
|
|
|
2020-08-06 07:26:25 -07:00
|
|
|
import "@openzeppelin/contracts/math/SafeMath.sol";
|
2020-08-20 07:58:03 -07:00
|
|
|
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
2020-08-03 06:09:40 -07:00
|
|
|
import "./BytesLib.sol";
|
2020-07-26 09:04:45 -07:00
|
|
|
|
2020-08-20 07:58:03 -07:00
|
|
|
contract Wormhole is ReentrancyGuard {
|
2020-08-03 06:09:40 -07:00
|
|
|
using BytesLib for bytes;
|
2020-07-26 09:04:45 -07:00
|
|
|
|
2020-08-03 06:09:40 -07:00
|
|
|
// Chain ID of Ethereum
|
2021-04-15 02:36:30 -07:00
|
|
|
uint8 public CHAIN_ID = 2;
|
2020-08-03 06:09:40 -07:00
|
|
|
|
|
|
|
struct GuardianSet {
|
2020-08-14 15:09:48 -07:00
|
|
|
address[] keys;
|
2020-08-03 06:09:40 -07:00
|
|
|
uint32 expiration_time;
|
2020-07-26 09:04:45 -07:00
|
|
|
}
|
|
|
|
|
2020-08-03 06:09:40 -07:00
|
|
|
event LogGuardianSetChanged(
|
2020-08-19 02:47:15 -07:00
|
|
|
uint32 oldGuardianIndex,
|
|
|
|
uint32 newGuardianIndex
|
2020-07-26 09:04:45 -07:00
|
|
|
);
|
|
|
|
|
2021-04-15 02:36:30 -07:00
|
|
|
event LogMessagePublished(
|
|
|
|
address emitter_address,
|
|
|
|
uint32 nonce,
|
|
|
|
bytes payload
|
2020-07-26 09:04:45 -07:00
|
|
|
);
|
|
|
|
|
2021-01-18 11:14:00 -08:00
|
|
|
struct ParsedVAA {
|
|
|
|
uint8 version;
|
|
|
|
bytes32 hash;
|
|
|
|
uint32 guardian_set_index;
|
|
|
|
uint32 timestamp;
|
|
|
|
uint8 action;
|
|
|
|
bytes payload;
|
|
|
|
}
|
|
|
|
|
2020-08-03 06:09:40 -07:00
|
|
|
// Mapping of guardian_set_index => guardian set
|
2020-08-06 14:32:31 -07:00
|
|
|
mapping(uint32 => GuardianSet) public guardian_sets;
|
2020-08-03 06:09:40 -07:00
|
|
|
// Current active guardian set
|
|
|
|
uint32 public guardian_set_index;
|
|
|
|
|
2020-08-27 23:49:46 -07:00
|
|
|
// Period for which a guardian set stays active after it has been replaced
|
|
|
|
uint32 public guardian_set_expirity;
|
2020-08-03 06:09:40 -07:00
|
|
|
|
|
|
|
// Mapping of already consumedVAAs
|
2020-08-28 06:10:42 -07:00
|
|
|
mapping(bytes32 => bool) public consumedVAAs;
|
2020-08-03 06:09:40 -07:00
|
|
|
|
2021-04-15 02:36:30 -07:00
|
|
|
constructor(GuardianSet memory initial_guardian_set, uint32 _guardian_set_expirity) public {
|
2020-08-03 06:09:40 -07:00
|
|
|
guardian_sets[0] = initial_guardian_set;
|
|
|
|
// Explicitly set for doc purposes
|
|
|
|
guardian_set_index = 0;
|
2020-08-27 23:49:46 -07:00
|
|
|
guardian_set_expirity = _guardian_set_expirity;
|
2020-08-03 06:09:40 -07:00
|
|
|
}
|
|
|
|
|
2020-08-18 07:19:28 -07:00
|
|
|
function getGuardianSet(uint32 idx) view public returns (GuardianSet memory gs) {
|
|
|
|
return guardian_sets[idx];
|
|
|
|
}
|
|
|
|
|
2021-04-15 02:36:30 -07:00
|
|
|
// Publish a message to be attested by the Wormhole network
|
|
|
|
function publishMessage(
|
|
|
|
uint32 nonce,
|
|
|
|
bytes memory payload
|
|
|
|
) public {
|
|
|
|
emit LogMessagePublished(msg.sender, nonce, payload);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enact a governance VAA
|
|
|
|
function executeGovernanceVAA(
|
2020-08-03 06:09:40 -07:00
|
|
|
bytes calldata vaa
|
2020-08-20 07:58:03 -07:00
|
|
|
) public nonReentrant {
|
2021-01-18 11:14:00 -08:00
|
|
|
ParsedVAA memory parsed_vaa = parseAndVerifyVAA(vaa);
|
|
|
|
// Process VAA
|
|
|
|
if (parsed_vaa.action == 0x01) {
|
|
|
|
require(parsed_vaa.guardian_set_index == guardian_set_index, "only the current guardian set can change the guardian set");
|
|
|
|
vaaUpdateGuardianSet(parsed_vaa.payload);
|
|
|
|
} else {
|
|
|
|
revert("invalid VAA action");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the VAA as consumed
|
|
|
|
consumedVAAs[parsed_vaa.hash] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// parseAndVerifyVAA parses raw VAA data into a struct and verifies whether it contains sufficient signatures of an
|
|
|
|
// active guardian set i.e. is valid according to Wormhole consensus rules.
|
|
|
|
function parseAndVerifyVAA(bytes calldata vaa) public view returns (ParsedVAA memory parsed_vaa) {
|
|
|
|
parsed_vaa.version = vaa.toUint8(0);
|
|
|
|
require(parsed_vaa.version == 1, "VAA version incompatible");
|
2020-08-03 06:09:40 -07:00
|
|
|
|
|
|
|
// Load 4 bytes starting from index 1
|
2021-01-18 11:14:00 -08:00
|
|
|
parsed_vaa.guardian_set_index = vaa.toUint32(1);
|
2020-08-03 06:09:40 -07:00
|
|
|
|
2020-08-14 15:09:48 -07:00
|
|
|
uint256 len_signers = vaa.toUint8(5);
|
|
|
|
uint offset = 6 + 66 * len_signers;
|
2020-07-26 09:04:45 -07:00
|
|
|
|
2020-08-14 15:09:48 -07:00
|
|
|
// Load 4 bytes timestamp
|
2021-01-18 11:14:00 -08:00
|
|
|
parsed_vaa.timestamp = vaa.toUint32(offset);
|
2020-07-26 09:04:45 -07:00
|
|
|
|
2020-08-03 06:09:40 -07:00
|
|
|
// Hash the body
|
2021-01-18 11:14:00 -08:00
|
|
|
parsed_vaa.hash = keccak256(vaa.slice(offset, vaa.length - offset));
|
|
|
|
require(!consumedVAAs[parsed_vaa.hash], "VAA was already executed");
|
2020-07-26 09:04:45 -07:00
|
|
|
|
2021-01-18 11:14:00 -08:00
|
|
|
GuardianSet memory guardian_set = guardian_sets[parsed_vaa.guardian_set_index];
|
2020-08-30 08:30:43 -07:00
|
|
|
require(guardian_set.keys.length > 0, "invalid guardian set");
|
2020-08-03 06:09:40 -07:00
|
|
|
require(guardian_set.expiration_time == 0 || guardian_set.expiration_time > block.timestamp, "guardian set has expired");
|
2020-10-28 13:22:15 -07:00
|
|
|
// We're using a fixed point number transformation with 1 decimal to deal with rounding.
|
|
|
|
require(((guardian_set.keys.length * 10 / 3) * 2) / 10 + 1 <= len_signers, "no quorum");
|
2020-08-14 15:09:48 -07:00
|
|
|
|
2020-08-30 08:30:43 -07:00
|
|
|
int16 last_index = - 1;
|
2020-08-14 15:09:48 -07:00
|
|
|
for (uint i = 0; i < len_signers; i++) {
|
|
|
|
uint8 index = vaa.toUint8(6 + i * 66);
|
2020-08-30 08:30:43 -07:00
|
|
|
require(index > last_index, "signature indices must be ascending");
|
|
|
|
last_index = int16(index);
|
|
|
|
|
2020-08-14 15:09:48 -07:00
|
|
|
bytes32 r = vaa.toBytes32(7 + i * 66);
|
|
|
|
bytes32 s = vaa.toBytes32(39 + i * 66);
|
|
|
|
uint8 v = vaa.toUint8(71 + i * 66);
|
|
|
|
v += 27;
|
2021-01-18 11:14:00 -08:00
|
|
|
require(ecrecover(parsed_vaa.hash, v, r, s) == guardian_set.keys[index], "VAA signature invalid");
|
2020-07-26 09:04:45 -07:00
|
|
|
}
|
2020-08-03 06:09:40 -07:00
|
|
|
|
2021-04-15 02:36:30 -07:00
|
|
|
parsed_vaa.payload = vaa.slice(offset + 4, vaa.length - (offset + 4));
|
2020-07-26 09:04:45 -07:00
|
|
|
}
|
|
|
|
|
2020-08-03 06:09:40 -07:00
|
|
|
function vaaUpdateGuardianSet(bytes memory data) private {
|
2020-08-14 15:09:48 -07:00
|
|
|
uint32 new_guardian_set_index = data.toUint32(0);
|
2020-08-31 00:25:41 -07:00
|
|
|
require(new_guardian_set_index == guardian_set_index + 1, "index must increase in steps of 1");
|
2020-08-14 15:09:48 -07:00
|
|
|
uint8 len = data.toUint8(4);
|
2020-08-03 06:09:40 -07:00
|
|
|
|
2020-08-14 15:09:48 -07:00
|
|
|
address[] memory new_guardians = new address[](len);
|
|
|
|
for (uint i = 0; i < len; i++) {
|
|
|
|
address addr = data.toAddress(5 + i * 20);
|
|
|
|
new_guardians[i] = addr;
|
|
|
|
}
|
2020-08-03 06:09:40 -07:00
|
|
|
|
|
|
|
uint32 old_guardian_set_index = guardian_set_index;
|
|
|
|
guardian_set_index = new_guardian_set_index;
|
2020-07-26 09:04:45 -07:00
|
|
|
|
2020-08-14 15:09:48 -07:00
|
|
|
GuardianSet memory new_guardian_set = GuardianSet(new_guardians, 0);
|
2020-08-03 06:09:40 -07:00
|
|
|
guardian_sets[guardian_set_index] = new_guardian_set;
|
2020-08-27 23:49:46 -07:00
|
|
|
guardian_sets[old_guardian_set_index].expiration_time = uint32(block.timestamp) + guardian_set_expirity;
|
2020-07-26 09:04:45 -07:00
|
|
|
|
2020-08-19 02:47:15 -07:00
|
|
|
emit LogGuardianSetChanged(old_guardian_set_index, guardian_set_index);
|
2020-07-26 09:04:45 -07:00
|
|
|
}
|
|
|
|
|
2021-04-15 02:36:30 -07:00
|
|
|
fallback() external payable {revert("unsupported");}
|
2020-07-26 09:04:45 -07:00
|
|
|
|
2021-04-15 02:36:30 -07:00
|
|
|
receive() external payable {revert("the Wormhole core does not accept assets");}
|
2020-07-26 09:04:45 -07:00
|
|
|
}
|