Relayer/upgradeable forward wrapper issue #2758 (pr #2767)

* grelayer: start #2758, create2 factory, merge lib & forward wrapper, forward wrapper stored as immutable var

* debug governance

* fw inherits from lib

* forge tests work

* cleanup

* fix typechain imports

* fix salt

* calldata packing :(

* relayer: things work

* relayer: moving code into corerelayerlibrary

* relayer: other file changes for code moving

* remove debugging statement

* fixing tilt deployment scripts

* relayer: relayer-engine does not start in ci

* passthrough number of guardians

* relayer: fix self signing script for multiple signers

* relayer fix ordering of guardian key load

---------

Co-authored-by: chase-45 <chasemoran45@gmail.com>
Co-authored-by: chase-45 <88348425+chase-45@users.noreply.github.com>
This commit is contained in:
Joe Howarth 2023-04-24 11:16:52 -04:00 committed by derpy-duck
parent 9dd4b7c67e
commit cb6fb1c4a2
45 changed files with 710 additions and 412 deletions

View File

@ -453,6 +453,7 @@ docker_build(
# ignore local node_modules (in case they're present)
ignore = ["./node_modules", "./ts-test"],
build_args = {"num_guardians": str(num_guardians)},
# sync external scripts for incremental development
# (everything else needs to be restarted from scratch for determinism)
@ -482,7 +483,7 @@ if spy_relayer or redis or generic_relayer or ci_tests:
k8s_yaml_with_ns("devnet/redis.yaml")
if generic_relayer or ci_tests:
if generic_relayer:
k8s_resource(
"relayer-engine",
resource_deps = ["guardian", "redis", "spy"],

View File

@ -59,3 +59,6 @@ RUN rm -rf node_modules && mv node_modules_cache node_modules
COPY --chown=node:node . .
RUN make forge_dependencies
ARG num_guardians
ENV NUM_GUARDIANS=$num_guardians

View File

@ -13,5 +13,9 @@ interface IForwardWrapper {
bytes[] memory signedVaas
) external payable returns (bool callToTargetContractSucceeded, uint256 transactionFeeRefundAmount);
function safeRelayProviderSupportsChain(IRelayProvider relayProvider, uint16 chainId) external view returns(bool isSupported);
function safeRelayProviderSupportsChain(IRelayProvider relayProvider, uint16 chainId)
external
view
returns (bool isSupported);
}

View File

@ -6,7 +6,7 @@ import "../../interfaces/relayer/IWormholeRelayer.sol";
import "./CoreRelayerDelivery.sol";
import "../../interfaces/relayer/IWormholeRelayerInternalStructs.sol";
contract CoreRelayer is CoreRelayerDelivery {
abstract contract CoreRelayer is CoreRelayerDelivery {
/**
* @notice This 'send' function emits a wormhole message (VAA) that alerts the default wormhole relay provider to
* call the 'deliver' endpoint of the contract on chain 'targetChain' and address 'targetAddress'

View File

@ -9,7 +9,7 @@ import "./CoreRelayerGovernance.sol";
import "../../interfaces/relayer/IWormholeRelayerInternalStructs.sol";
import "./CoreRelayerMessages.sol";
contract CoreRelayerDelivery is CoreRelayerGovernance {
abstract contract CoreRelayerDelivery is CoreRelayerGovernance {
enum DeliveryStatus {
SUCCESS,
RECEIVER_FAILURE,

View File

@ -9,19 +9,14 @@ import "../../interfaces/relayer/IWormholeRelayerInternalStructs.sol";
import "./CoreRelayerState.sol";
import "../../libraries/external/BytesLib.sol";
contract CoreRelayerGetters is CoreRelayerState {
abstract contract CoreRelayerGetters is CoreRelayerState {
using BytesLib for bytes;
function governanceActionIsConsumed(bytes32 hash) public view returns (bool) {
return _state.consumedGovernanceActions[hash];
}
// immutable var set in implementation, not proxy state
address immutable forwardWrapper;
function governanceChainId() public view returns (uint16) {
return _state.provider.governanceChainId;
}
function governanceContract() public view returns (bytes32) {
return _state.provider.governanceContract;
constructor(address _forwardWrapper) {
forwardWrapper = _forwardWrapper;
}
function isInitialized(address impl) public view returns (bool) {
@ -52,12 +47,16 @@ contract CoreRelayerGetters is CoreRelayerState {
return _state.defaultRelayProvider;
}
function getForwardInstructions() public view returns (IWormholeRelayerInternalStructs.ForwardInstruction[] memory) {
function getForwardInstructions()
public
view
returns (IWormholeRelayerInternalStructs.ForwardInstruction[] memory)
{
return _state.forwardInstructions;
}
function getWormholeRelayerCallerAddress() public view returns (address) {
return _state.forwardWrapper;
return forwardWrapper;
}
function isContractLocked() internal view returns (bool) {

View File

@ -2,17 +2,17 @@
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
import "../../libraries/external/BytesLib.sol";
import "./CoreRelayerGetters.sol";
import "./CoreRelayerSetters.sol";
import "../../interfaces/relayer/IWormholeRelayerInternalStructs.sol";
import "../../interfaces/relayer/IForwardWrapper.sol";
import "./CoreRelayerMessages.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
import "../../interfaces/IWormhole.sol";
import "./CoreRelayerLibrary.sol";
abstract contract CoreRelayerGovernance is
CoreRelayerGetters,
@ -22,109 +22,19 @@ abstract contract CoreRelayerGovernance is
{
using BytesLib for bytes;
error InvalidFork();
error InvalidGovernanceVM(string reason);
error WrongChainId(uint16 chainId);
error InvalidChainId(uint16 chainId);
error FailedToInitializeImplementation(string reason);
event ContractUpgraded(address indexed oldContract, address indexed newContract);
// "CoreRelayer" (left padded)
bytes32 constant module = 0x000000000000000000000000000000000000000000436f726552656c61796572;
function submitContractUpgrade(bytes memory _vm) public {
if (isFork()) {
revert InvalidFork();
}
(IWormhole.VM memory vm, bool valid, string memory reason) = verifyGovernanceVM(_vm);
if (!valid) {
revert InvalidGovernanceVM(string(reason));
}
setConsumedGovernanceAction(vm.hash);
CoreRelayerLibrary.ContractUpgrade memory contractUpgrade = CoreRelayerLibrary.parseUpgrade(vm.payload, module);
if (contractUpgrade.chain != chainId()) {
revert WrongChainId(contractUpgrade.chain);
}
upgradeImplementation(contractUpgrade.newContract);
function submitContractUpgrade(bytes memory vaa) public {
(bool success, bytes memory reason) = getWormholeRelayerCallerAddress().delegatecall(abi.encodeWithSignature("submitContractUpgrade(bytes)", vaa));
require(success, string(reason));
}
function registerCoreRelayerContract(bytes memory vaa) public {
(IWormhole.VM memory vm, bool valid, string memory reason) = verifyGovernanceVM(vaa);
if (!valid) {
revert InvalidGovernanceVM(string(reason));
}
setConsumedGovernanceAction(vm.hash);
CoreRelayerLibrary.RegisterChain memory rc = CoreRelayerLibrary.parseRegisterChain(vm.payload, module);
if ((rc.chain != chainId() || isFork()) && rc.chain != 0) {
revert InvalidChainId(rc.chain);
}
setRegisteredCoreRelayerContract(rc.emitterChain, rc.emitterAddress);
(bool success, bytes memory reason) = getWormholeRelayerCallerAddress().delegatecall(abi.encodeWithSignature("registerCoreRelayerContract(bytes)", vaa));
require(success, string(reason));
}
function setDefaultRelayProvider(bytes memory vaa) public {
(IWormhole.VM memory vm, bool valid, string memory reason) = verifyGovernanceVM(vaa);
if (!valid) {
revert InvalidGovernanceVM(string(reason));
}
setConsumedGovernanceAction(vm.hash);
CoreRelayerLibrary.UpdateDefaultProvider memory provider =
CoreRelayerLibrary.parseUpdateDefaultProvider(vm.payload, module);
if ((provider.chain != chainId() || isFork()) && provider.chain != 0) {
revert InvalidChainId(provider.chain);
}
setRelayProvider(provider.newProvider);
(bool success, bytes memory reason) = getWormholeRelayerCallerAddress().delegatecall(abi.encodeWithSignature("setDefaultRelayProvider(bytes)", vaa));
require(success, string(reason));
}
function upgradeImplementation(address newImplementation) internal {
address currentImplementation = _getImplementation();
_upgradeTo(newImplementation);
// Call initialize function of the new implementation
(bool success, bytes memory reason) = newImplementation.delegatecall(abi.encodeWithSignature("initialize()"));
if (!success) {
revert FailedToInitializeImplementation(string(reason));
}
emit ContractUpgraded(currentImplementation, newImplementation);
}
function verifyGovernanceVM(bytes memory encodedVM)
internal
view
returns (IWormhole.VM memory parsedVM, bool isValid, string memory invalidReason)
{
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole().parseAndVerifyVM(encodedVM);
if (!valid) {
return (vm, valid, reason);
}
if (vm.emitterChainId != governanceChainId()) {
return (vm, false, "wrong governance chain");
}
if (vm.emitterAddress != governanceContract()) {
return (vm, false, "wrong governance contract");
}
if (governanceActionIsConsumed(vm.hash)) {
return (vm, false, "governance action already consumed");
}
return (vm, true, "");
}
}

View File

@ -9,6 +9,8 @@ import "./CoreRelayer.sol";
contract CoreRelayerImplementation is CoreRelayer {
error ImplementationAlreadyInitialized();
constructor(address _forwardWrapper) CoreRelayerGetters(_forwardWrapper) {}
function initialize() public virtual initializer {
// this function needs to be exposed for an upgrade to pass
}

View File

@ -1,11 +1,52 @@
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "../../libraries/external/BytesLib.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
library CoreRelayerLibrary {
import "../../libraries/external/BytesLib.sol";
import "../../interfaces/relayer/IForwardWrapper.sol";
import "../../interfaces/IWormhole.sol";
import "./CoreRelayerState.sol";
import "../../interfaces/relayer/IForwardInstructionViewer.sol";
contract CoreRelayerLibrary is
CoreRelayerState,
ERC1967Upgrade {
using BytesLib for bytes;
//structs, consts, errors, events
struct ContractUpgrade {
bytes32 module;
uint8 action;
uint16 chain;
address newContract;
}
struct RegisterChain {
bytes32 module;
uint8 action;
uint16 chain; //TODO Why is this on this object?
uint16 emitterChain;
bytes32 emitterAddress;
}
//This could potentially be combined with ContractUpgrade
struct UpdateDefaultProvider {
bytes32 module;
uint8 action;
uint16 chain;
address newProvider;
}
bytes32 constant module = 0x000000000000000000000000000000000000000000436f726552656c61796572;
IForwardInstructionViewer public immutable forwardInstructionViewer;
IWormhole immutable wormhole;
error InvalidFork();
error InvalidGovernanceVM(string reason);
error WrongChainId(uint16 chainId);
error InvalidChainId(uint16 chainId);
error FailedToInitializeImplementation(string reason);
error WrongModule(bytes32 module);
error InvalidContractUpgradeAction(uint8 action);
error InvalidContractUpgradeLength(uint256 length);
@ -13,9 +54,86 @@ library CoreRelayerLibrary {
error InvalidRegisterChainLength(uint256);
error InvalidDefaultProviderAction(uint8);
error InvalidDefaultProviderLength(uint256);
error RequesterNotCoreRelayer();
function parseUpgrade(bytes memory encodedUpgrade, bytes32 module)
public
event ContractUpgraded(address indexed oldContract, address indexed newContract);
//This modifier is used to ensure that only the wormhole relayer can call the functions in this contract via delegate call
modifier onlyWormholeRelayer() {
if (address(this) != address(forwardInstructionViewer)) {
revert RequesterNotCoreRelayer();
}
_;
}
constructor(address _wormholeRelayer, address _wormhole) {
forwardInstructionViewer = IForwardInstructionViewer(_wormholeRelayer);
wormhole = IWormhole(_wormhole);
}
//external functions
function submitContractUpgrade(bytes memory _vm) external onlyWormholeRelayer {
if (isFork()) {
revert InvalidFork();
}
(IWormhole.VM memory vm, bool valid, string memory reason) = verifyGovernanceVM(_vm);
if (!valid) {
revert InvalidGovernanceVM(string(reason));
}
setConsumedGovernanceAction(vm.hash);
ContractUpgrade memory contractUpgrade = parseUpgrade(vm.payload);
if (contractUpgrade.chain != chainId()) {
revert WrongChainId(contractUpgrade.chain);
}
upgradeImplementation(contractUpgrade.newContract);
}
function registerCoreRelayerContract(bytes memory vaa) external onlyWormholeRelayer {
(IWormhole.VM memory vm, bool valid, string memory reason) = verifyGovernanceVM(vaa);
if (!valid) {
revert InvalidGovernanceVM(string(reason));
}
setConsumedGovernanceAction(vm.hash);
RegisterChain memory rc = parseRegisterChain(vm.payload);
if ((rc.chain != chainId() || isFork()) && rc.chain != 0) {
revert InvalidChainId(rc.chain);
}
setRegisteredCoreRelayerContract(rc.emitterChain, rc.emitterAddress);
}
function setDefaultRelayProvider(bytes memory vaa) external onlyWormholeRelayer {
(IWormhole.VM memory vm, bool valid, string memory reason) = verifyGovernanceVM(vaa);
if (!valid) {
revert InvalidGovernanceVM(string(reason));
}
setConsumedGovernanceAction(vm.hash);
UpdateDefaultProvider memory provider = parseUpdateDefaultProvider(vm.payload);
if ((provider.chain != chainId() || isFork()) && provider.chain != 0) {
revert InvalidChainId(provider.chain);
}
setRelayProvider(provider.newProvider);
}
//parser functions
function parseUpgrade(bytes memory encodedUpgrade)
internal
pure
returns (ContractUpgrade memory cu)
{
@ -46,8 +164,8 @@ library CoreRelayerLibrary {
}
}
function parseRegisterChain(bytes memory encodedRegistration, bytes32 module)
public
function parseRegisterChain(bytes memory encodedRegistration)
internal
pure
returns (RegisterChain memory registerChain)
{
@ -81,8 +199,8 @@ library CoreRelayerLibrary {
}
}
function parseUpdateDefaultProvider(bytes memory encodedDefaultProvider, bytes32 module)
public
function parseUpdateDefaultProvider(bytes memory encodedDefaultProvider)
internal
pure
returns (UpdateDefaultProvider memory defaultProvider)
{
@ -113,26 +231,105 @@ library CoreRelayerLibrary {
}
}
struct ContractUpgrade {
bytes32 module;
uint8 action;
uint16 chain;
address newContract;
//helper functions
function upgradeImplementation(address newImplementation) internal {
address currentImplementation = _getImplementation();
_upgradeTo(newImplementation);
// Call initialize function of the new implementation
(bool success, bytes memory reason) = newImplementation.delegatecall(abi.encodeWithSignature("initialize()"));
if (!success) {
revert FailedToInitializeImplementation(string(reason));
}
emit ContractUpgraded(currentImplementation, newImplementation);
}
struct RegisterChain {
bytes32 module;
uint8 action;
uint16 chain; //TODO Why is this on this object?
uint16 emitterChain;
bytes32 emitterAddress;
function verifyGovernanceVM(bytes memory encodedVM)
internal
view
returns (IWormhole.VM memory parsedVM, bool isValid, string memory invalidReason)
{
(IWormhole.VM memory vm, bool valid, string memory reason) = getWormholeState().parseAndVerifyVM(encodedVM);
if (!valid) {
return (vm, valid, reason);
}
if (vm.emitterChainId != governanceChainId()) {
return (vm, false, "wrong governance chain");
}
if (vm.emitterAddress != governanceContract()) {
return (vm, false, "wrong governance contract");
}
if (governanceActionIsConsumed(vm.hash)) {
return (vm, false, "governance action already consumed");
}
return (vm, true, "");
}
//This could potentially be combined with ContractUpgrade
struct UpdateDefaultProvider {
bytes32 module;
uint8 action;
uint16 chain;
address newProvider;
//setters
function setConsumedGovernanceAction(bytes32 hash) internal {
_state.consumedGovernanceActions[hash] = true;
}
function setRelayProvider(address defaultRelayProvider) internal {
_state.defaultRelayProvider = defaultRelayProvider;
}
function setRegisteredCoreRelayerContract(uint16 targetChain, bytes32 relayerAddress) internal {
_state.registeredCoreRelayerContract[targetChain] = relayerAddress;
}
//getters
function getWormholeState() internal view returns (IWormhole) {
return IWormhole(_state.provider.wormhole);
}
function chainId() internal view returns (uint16) {
return _state.provider.chainId;
}
function evmChainId() internal view returns (uint256) {
return _state.evmChainId;
}
function isFork() internal view returns (bool) {
return evmChainId() != block.chainid;
}
function governanceActionIsConsumed(bytes32 hash) internal view returns (bool) {
return _state.consumedGovernanceActions[hash];
}
function governanceChainId() internal view returns (uint16) {
return _state.provider.governanceChainId;
}
function governanceContract() internal view returns (bytes32) {
return _state.provider.governanceContract;
}
}

View File

@ -9,7 +9,7 @@ import "../../interfaces/relayer/IWormholeRelayerInternalStructs.sol";
import "../../interfaces/relayer/IWormholeRelayer.sol";
import "../../interfaces/relayer/IDelivery.sol";
contract CoreRelayerMessages is CoreRelayerGetters {
abstract contract CoreRelayerMessages is CoreRelayerGetters {
using BytesLib for bytes;
error InvalidPayloadId(uint8 payloadId);

View File

@ -5,5 +5,5 @@ pragma solidity ^0.8.0;
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
contract CoreRelayerProxy is ERC1967Proxy {
constructor(address implementation, bytes memory initData) ERC1967Proxy(implementation, initData) {}
constructor(address implementation) ERC1967Proxy(implementation, new bytes(0)) {}
}

View File

@ -38,10 +38,6 @@ contract CoreRelayerSetters is CoreRelayerState, Context {
_state.defaultRelayProvider = defaultRelayProvider;
}
function setRegisteredCoreRelayerContract(uint16 chainId, bytes32 relayerAddress) internal {
_state.registeredCoreRelayerContract[chainId] = relayerAddress;
}
function appendForwardInstruction(IWormholeRelayerInternalStructs.ForwardInstruction memory forwardInstruction) internal {
_state.forwardInstructions.push(forwardInstruction);
}
@ -58,10 +54,6 @@ contract CoreRelayerSetters is CoreRelayerState, Context {
_state.targetAddress = targetAddress;
}
function setForwardWrapper(address newForwardWrapperAddress) internal {
_state.forwardWrapper = newForwardWrapperAddress;
}
function setEvmChainId(uint256 evmChainId) internal {
if (evmChainId != block.chainid) {
revert InvalidEvmChainId();

View File

@ -3,7 +3,6 @@
pragma solidity ^0.8.0;
import "./CoreRelayerGovernance.sol";
import "./ForwardWrapper.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
@ -34,16 +33,12 @@ contract CoreRelayerSetup is CoreRelayerSetters, ERC1967Upgrade {
}
setChainId(chainId);
setEvmChainId(evmChainId);
setGovernanceChainId(governanceChainId);
setWormhole(wormhole);
setRelayProvider(defaultRelayProvider);
setGovernanceChainId(governanceChainId);
setGovernanceContract(governanceContract);
setEvmChainId(evmChainId);
setForwardWrapper(address(new ForwardWrapper(address(this), wormhole)));
_upgradeTo(implementation);

View File

@ -26,8 +26,6 @@ contract CoreRelayerStorage {
address defaultRelayProvider;
// Requests which will be forwarded from the current delivery.
IWormholeRelayerInternalStructs.ForwardInstruction[] forwardInstructions;
// Wrapper contract to facilitate forwards
address forwardWrapper;
// mapping of initialized implementations
mapping(address => bool) initializedImplementations;
// mapping of relayer contracts on other chains

View File

@ -10,17 +10,13 @@ import "../../interfaces/relayer/IWormholeRelayerInternalStructs.sol";
import "../../interfaces/relayer/IForwardWrapper.sol";
import "../../interfaces/relayer/IWormholeReceiver.sol";
import "../../interfaces/relayer/IRelayProvider.sol";
import {CoreRelayerLibrary} from "../coreRelayer/CoreRelayerLibrary.sol";
contract ForwardWrapper {
IForwardInstructionViewer forwardInstructionViewer;
IWormhole wormhole;
contract ForwardWrapper is CoreRelayerLibrary {
error RequesterNotCoreRelayer();
error ForwardNotSufficientlyFunded(uint256 amountOfFunds, uint256 amountOfFundsNeeded);
constructor(address _wormholeRelayer, address _wormhole) {
forwardInstructionViewer = IForwardInstructionViewer(_wormholeRelayer);
wormhole = IWormhole(_wormhole);
constructor(address _wormholeRelayer, address _wormhole) CoreRelayerLibrary( _wormholeRelayer, _wormhole) {
}
function executeInstruction(
@ -56,7 +52,7 @@ contract ForwardWrapper {
if (forwardInstructions.length > 0) {
uint256 totalMsgValue = 0;
uint256 totalFee = 0;
for(uint8 i=0; i<forwardInstructions.length; i++) {
for (uint8 i = 0; i < forwardInstructions.length; i++) {
totalMsgValue += forwardInstructions[i].msgValue;
totalFee += forwardInstructions[i].totalFee;
}
@ -71,7 +67,12 @@ contract ForwardWrapper {
}
}
function safeRelayProviderSupportsChain(IRelayProvider relayProvider, uint16 chainId) view external returns (bool isSupported){
function safeRelayProviderSupportsChain(IRelayProvider relayProvider, uint16 chainId)
external
view
returns (bool isSupported)
{
return relayProvider.isChainSupported(chainId);
}
}

View File

@ -0,0 +1,31 @@
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/Create2.sol";
/**
* Contract factory that facilitates predfictable deployment addresses
*/
contract Create2Factory {
event Created(address);
/// @dev create2 hashes the userSalt with msg.sender, then uses the CREATE2 opcode to deterministically create a contract
function create2(bytes memory userSalt, bytes memory bytecode) public payable returns (address payable) {
address addr = Create2.deploy(msg.value, salt(msg.sender, userSalt), bytecode);
emit Created(addr);
return payable(addr);
}
function computeAddress(address creator, bytes memory userSalt, bytes32 bytecodeHash)
public
view
returns (address)
{
return Create2.computeAddress(salt(creator, userSalt), bytecodeHash);
}
function salt(address creator, bytes memory userSalt) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(creator, userSalt));
}
}

View File

@ -8,7 +8,9 @@ import {RelayProviderSetup} from "../../contracts/relayer/relayProvider/RelayPro
import {RelayProviderImplementation} from "../../contracts/relayer/relayProvider/RelayProviderImplementation.sol";
import {RelayProviderProxy} from "../../contracts/relayer/relayProvider/RelayProviderProxy.sol";
import {IWormholeRelayer} from "../../contracts/interfaces/relayer/IWormholeRelayer.sol";
import {ForwardWrapper} from "../../contracts/relayer/coreRelayer/ForwardWrapper.sol";
import {CoreRelayer} from "../../contracts/relayer/coreRelayer/CoreRelayer.sol";
import {Create2Factory} from "../../contracts/relayer/create2Factory/Create2Factory.sol";
import {CoreRelayerSetup} from "../../contracts/relayer/coreRelayer/CoreRelayerSetup.sol";
import {CoreRelayerImplementation} from "../../contracts/relayer/coreRelayer/CoreRelayerImplementation.sol";
import {CoreRelayerProxy} from "../../contracts/relayer/coreRelayer/CoreRelayerProxy.sol";
@ -106,23 +108,34 @@ contract TestHelpers {
public
returns (IWormholeRelayer coreRelayer)
{
CoreRelayerSetup coreRelayerSetup = new CoreRelayerSetup();
CoreRelayerImplementation coreRelayerImplementation = new CoreRelayerImplementation();
CoreRelayerProxy myCoreRelayer = new CoreRelayerProxy(
address(coreRelayerSetup),
abi.encodeCall(
CoreRelayerSetup.setup,
(
address(coreRelayerImplementation),
chainId,
address(wormhole),
defaultRelayProvider,
wormhole.governanceChainId(),
wormhole.governanceContract(),
block.chainid
)
Create2Factory create2Factory = new Create2Factory();
CoreRelayerSetup coreRelayerSetup =
CoreRelayerSetup(create2Factory.create2("0xSetup", type(CoreRelayerSetup).creationCode));
address proxyAddressComputed = create2Factory.computeAddress(
address(this),
"0xGenericRelayer",
keccak256(abi.encodePacked(type(CoreRelayerProxy).creationCode, abi.encode(address(coreRelayerSetup))))
);
ForwardWrapper forwardWrapper = new ForwardWrapper(proxyAddressComputed, address(wormhole));
CoreRelayerImplementation coreRelayerImplementation = new CoreRelayerImplementation(address(forwardWrapper));
CoreRelayerProxy myCoreRelayer = CoreRelayerProxy(
create2Factory.create2(
"0xGenericRelayer",
abi.encodePacked(type(CoreRelayerProxy).creationCode, abi.encode(address(coreRelayerSetup)))
)
);
CoreRelayerSetup(address(myCoreRelayer)).setup(
address(coreRelayerImplementation),
chainId,
address(wormhole),
defaultRelayProvider,
wormhole.governanceChainId(),
wormhole.governanceContract(),
block.chainid
);
coreRelayer = IWormholeRelayer(address(myCoreRelayer));
}
}

View File

@ -17,6 +17,7 @@ import {CoreRelayerSetup} from "../../contracts/relayer/coreRelayer/CoreRelayerS
import {CoreRelayerImplementation} from "../../contracts/relayer/coreRelayer/CoreRelayerImplementation.sol";
import {CoreRelayerProxy} from "../../contracts/relayer/coreRelayer/CoreRelayerProxy.sol";
import {CoreRelayerMessages} from "../../contracts/relayer/coreRelayer/CoreRelayerMessages.sol";
import {ForwardWrapper} from "../../contracts/relayer/coreRelayer/ForwardWrapper.sol";
import {CoreRelayerGovernance} from "../../contracts/relayer/coreRelayer/CoreRelayerGovernance.sol";
import {MockGenericRelayer} from "./MockGenericRelayer.sol";
import {MockWormhole} from "./MockWormhole.sol";
@ -159,26 +160,11 @@ contract WormholeRelayerGovernanceTests is Test {
}
function testUpgradeContractToItself() public {
CoreRelayerSetup coreRelayerSetup = new CoreRelayerSetup();
CoreRelayerImplementation coreRelayerImplementation = new CoreRelayerImplementation();
CoreRelayerProxy myCoreRelayer = new CoreRelayerProxy(
address(coreRelayerSetup),
abi.encodeCall(
CoreRelayerSetup.setup,
(
address(coreRelayerImplementation),
1,
address(wormhole),
address(relayProvider),
wormhole.governanceChainId(),
wormhole.governanceContract(),
block.chainid
)
)
);
address myCoreRelayer = address(helpers.setUpCoreRelayer(wormhole.chainId(), wormhole, address(relayProvider)));
for (uint256 i = 0; i < 10; i++) {
CoreRelayerImplementation coreRelayerImplementationNew = new CoreRelayerImplementation();
address forwardWrapper = address(new ForwardWrapper(myCoreRelayer, address(wormhole)));
CoreRelayerImplementation coreRelayerImplementationNew = new CoreRelayerImplementation(forwardWrapper);
bytes memory message = abi.encodePacked(
relayerModule,

View File

@ -37,8 +37,7 @@
"abigen": "truffle run abigen",
"deploy-relayers-evm1": "ENV=kubernetes CONTAINER=evm1 bash ./ts-scripts/relayer/shell/deployInContainer.sh",
"deploy-relayers-evm2": "ENV=kubernetes CONTAINER=evm2 bash ./ts-scripts/relayer/shell/deployInContainer.sh",
"relayer-tilt-test": "ENV=tilt WALLET_KEY=4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d npx ts-mocha -t 1000000 ts-test/relayer/*.ts",
"relayer-tilt-test-other": "ENV=tilt WALLET_KEY=4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d ts-mocha -t 1000000 ts-test/relayer/*.ts",
"relayer-tilt-test": "ENV=tilt WALLET_KEY=4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d ts-mocha -t 1000000 ts-test/relayer/*.ts",
"relayer-ci-test": "ENV=ci WALLET_KEY=4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d npx ts-mocha -t 1000000 ts-test/relayer/*.ts"
},
"author": "",

5
ethereum/remappings.txt Normal file
View File

@ -0,0 +1,5 @@
@openzeppelin/=node_modules/@openzeppelin/
@solidity-parser/=node_modules/@solidity-parser/
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
truffle/=node_modules/truffle/

View File

@ -1,4 +1,5 @@
GUARDIAN_KEY=cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0
GUARDIAN_KEY2=c3b2e45c422a1602333a64078aeb42637370b0f48fe385f9cfa6ad54a8e0c47e
WALLET_KEY=0x6370fd033278c143179d81c5526140625662b8daa446c22ee2d73db3707e620c
#the wallet key is private key 2 on the ganache instance, it is a different private key than is used for other deployments, which is private key 0.
# 0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b is the public address

View File

@ -1 +1 @@
output/*
output/**/*

View File

@ -41,7 +41,7 @@ async function main() {
throw Error("Failed to find contracts file for this process!");
}
const contracts = JSON.parse(contractsFile.toString());
contracts.lastRun = true;
contracts.useLastRun = true;
writeFileSync(path, JSON.stringify(contracts, undefined, 2));
}
}

View File

@ -4,31 +4,41 @@
"relayProviders": [
{
"chainId": 2,
"address": "0x83752ecAfeBf4707258dedFFbD9c7443148169db"
"address": "0x1ef9e15c3bbf0555860b5009B51722027134d53a"
},
{
"chainId": 4,
"address": "0x83752ecAfeBf4707258dedFFbD9c7443148169db"
"address": "0x1ef9e15c3bbf0555860b5009B51722027134d53a"
}
],
"coreRelayers": [
{
"chainId": 2,
"address": "0x0eb0dD3aa41bD15C706BC09bC03C002b7B85aeAC"
"address": "0xFD66d75a5EF7755ef99345B344730af7cE47D2cD"
},
{
"chainId": 4,
"address": "0x0eb0dD3aa41bD15C706BC09bC03C002b7B85aeAC"
"address": "0xFD66d75a5EF7755ef99345B344730af7cE47D2cD"
}
],
"mockIntegrations": [
{
"chainId": 2,
"address": "0xd80054abb128B007A6ef286b38D8082d50839bF7"
"address": "0xD697292d18f9c6bc9e2aD089e823d562eD4A9766"
},
{
"chainId": 4,
"address": "0xd80054abb128B007A6ef286b38D8082d50839bF7"
"address": "0xD697292d18f9c6bc9e2aD089e823d562eD4A9766"
}
],
"create2Factories": [
{
"chainId": 2,
"address": "0x17e91224c30c5b0B13ba2ef1E84FE880Cb902352"
},
{
"chainId": 4,
"address": "0x17e91224c30c5b0B13ba2ef1E84FE880Cb902352"
}
]
}

View File

@ -4,31 +4,41 @@
"relayProviders": [
{
"chainId": 2,
"address": "0x83752ecAfeBf4707258dedFFbD9c7443148169db"
"address": "0x1ef9e15c3bbf0555860b5009B51722027134d53a"
},
{
"chainId": 4,
"address": "0x83752ecAfeBf4707258dedFFbD9c7443148169db"
"address": "0x1ef9e15c3bbf0555860b5009B51722027134d53a"
}
],
"coreRelayers": [
{
"chainId": 2,
"address": "0x0eb0dD3aa41bD15C706BC09bC03C002b7B85aeAC"
"address": "0xFD66d75a5EF7755ef99345B344730af7cE47D2cD"
},
{
"chainId": 4,
"address": "0x0eb0dD3aa41bD15C706BC09bC03C002b7B85aeAC"
"address": "0xFD66d75a5EF7755ef99345B344730af7cE47D2cD"
}
],
"mockIntegrations": [
{
"chainId": 2,
"address": "0xd80054abb128B007A6ef286b38D8082d50839bF7"
"address": "0xD697292d18f9c6bc9e2aD089e823d562eD4A9766"
},
{
"chainId": 4,
"address": "0xd80054abb128B007A6ef286b38D8082d50839bF7"
"address": "0xD697292d18f9c6bc9e2aD089e823d562eD4A9766"
}
],
"create2Factories": [
{
"chainId": 2,
"address": "0x17e91224c30c5b0B13ba2ef1E84FE880Cb902352"
},
{
"chainId": 4,
"address": "0x17e91224c30c5b0B13ba2ef1E84FE880Cb902352"
}
]
}

View File

@ -1,6 +1,7 @@
import { readFileSync, writeFileSync } from "fs";
import {
getCoreRelayer,
getCreate2Factory,
getMockIntegration,
getRelayProvider,
init,
@ -18,6 +19,7 @@ interface ContractsJson {
relayProviders: Address[];
coreRelayers: Address[];
mockIntegrations: Address[];
create2Factories: Address[];
}
async function main() {
@ -29,6 +31,7 @@ async function main() {
contracts.relayProviders = [] as any;
contracts.coreRelayers = [] as any;
contracts.mockIntegrations = [] as any;
contracts.create2Factories = [] as any;
for (const chain of chains) {
update(contracts.relayProviders, {
chainId: chain.chainId,
@ -36,12 +39,16 @@ async function main() {
});
update(contracts.coreRelayers, {
chainId: chain.chainId,
address: getCoreRelayer(chain).address,
address: (await getCoreRelayer(chain)).address,
});
update(contracts.mockIntegrations, {
chainId: chain.chainId,
address: getMockIntegration(chain).address,
});
update(contracts.create2Factories, {
chainId: chain.chainId,
address: getCreate2Factory(chain).address,
});
}
const newStr = JSON.stringify(contracts, undefined, 2);
console.log("New:");

View File

@ -1,34 +1,44 @@
{
"description": "This file contains the addresses for the contracts on each chain. If useLastRun is true, this file will be ignored, and the addresses will be taken from the lastrun.json of the deployment scripts.",
"useLastRun": false,
"useLastRun": true,
"relayProviders": [
{
"chainId": 6,
"address": "0x79357aA0CC8f1B4Bf3AC51Ae365cED1A83850870"
"address": "0x58DFf38a3E97568CA2acfd95ca0901E8B4D3D3A8"
},
{
"chainId": 14,
"address": "0xd5A128895A4fe5b1E9615755698F65e45E42ee2f"
"address": "0x64847cD62c8116231BD082d0b3E0eC93F205A7A3"
}
],
"coreRelayers": [
{
"chainId": 6,
"address": "0x607cE9D0f9Cd772Dab917507C331257FF561DBA5"
"address": "0xeF5d96cC539dae286513A498f54B17f8b49ecae2"
},
{
"chainId": 14,
"address": "0x38bCfa6D311154824Bb98EE9D7e9bD6f5C706aab"
"address": "0x4406CE49fD696C5eB1b30dfece71eb1b9322514d"
}
],
"mockIntegrations": [
{
"chainId": 6,
"address": "0x05cDBE523b45a33e866C5199394C7C559816CDaD"
"address": "0x66dd3fBFf7fF50C074B6Bb159519dCa98ECC552F"
},
{
"chainId": 14,
"address": "0xC8865eFc8F73a8A3C3e590Bee9491F0EB5bf66b2"
"address": "0xb9E73a5c623f241a6a676BA830cfB0C677b37ca1"
}
],
"create2Factories": [
{
"chainId": 6,
"address": "0xBD49c3FEd4d972D2125D6B12DaA93881E92eaA2d"
},
{
"chainId": 14,
"address": "0x030005bA700CE16b13c249B8029fc7d8f4e0b134"
}
]
}

View File

@ -1,6 +1,6 @@
{
"description": "This file contains the addresses for the contracts on each chain. If useLastRun is true, this file will be ignored, and the addresses will be taken from the lastrun.json of the deployment scripts.",
"useLastRun": false,
"useLastRun": true,
"relayProviders": [
{
"chainId": 2,

View File

@ -1,15 +1,15 @@
import {
deployCoreRelayerImplementation,
deployCoreRelayerLibrary,
deployCoreRelayerProxy,
deployCoreRelayerSetup,
deployForwardWrapper,
} from "../helpers/deployments";
import {
init,
loadChains,
writeOutputFiles,
getRelayProviderAddress,
getOperatingChains,
getCoreRelayerAddress,
} from "../helpers/env";
const processName = "deployCoreRelayer";
@ -28,10 +28,14 @@ async function run() {
for (const chain of chains) {
console.log(`Deploying for chain ${chain.chainId}...`);
const coreRelayerLibrary = await deployCoreRelayerLibrary(chain);
const forwardWrapper = await deployForwardWrapper(
chain,
// uses create2 to determine address before deployment
await getCoreRelayerAddress(chain)
);
const coreRelayerImplementation = await deployCoreRelayerImplementation(
chain,
coreRelayerLibrary.address
forwardWrapper.address
);
const coreRelayerSetup = await deployCoreRelayerSetup(chain);
const coreRelayerProxy = await deployCoreRelayerProxy(
@ -42,7 +46,7 @@ async function run() {
getRelayProviderAddress(chain)
);
output.coreRelayerLibraries.push(coreRelayerLibrary);
output.coreRelayerLibraries.push(forwardWrapper);
output.coreRelayerImplementations.push(coreRelayerImplementation);
output.coreRelayerSetups.push(coreRelayerSetup);
output.coreRelayerProxies.push(coreRelayerProxy);

View File

@ -1,37 +0,0 @@
import {
deployCoreRelayerImplementation,
deployCoreRelayerLibrary,
} from "../helpers/deployments";
import {
getOperatingChains,
init,
loadChains,
writeOutputFiles,
} from "../helpers/env";
const processName = "deployCoreRelayerImpl";
init();
const chains = getOperatingChains();
async function run() {
console.log("Start! " + processName);
const output: any = {
coreRelayerLibraries: [],
coreRelayerImplementations: [],
};
for (let i = 0; i < chains.length; i++) {
const coreRelayerLibrary = await deployCoreRelayerLibrary(chains[i]);
const coreRelayerImplementation = await deployCoreRelayerImplementation(
chains[i],
coreRelayerLibrary.address
);
output.coreRelayerImplementations.push(coreRelayerImplementation);
output.coreRelayerLibraries.push(coreRelayerLibrary);
}
writeOutputFiles(output, processName);
}
run().then(() => console.log("Done! " + processName));

View File

@ -50,8 +50,8 @@ async function readState(
);
try {
const coreRelayer = getCoreRelayer(chain, getProvider(chain));
const contractAddress = getCoreRelayerAddress(chain);
const coreRelayer = await getCoreRelayer(chain, getProvider(chain));
const contractAddress = await getCoreRelayerAddress(chain);
const defaultProvider = await coreRelayer.getDefaultRelayProvider();
const registeredContracts: { chainId: number; contract: string }[] = [];
@ -59,7 +59,7 @@ async function readState(
registeredContracts.push({
chainId: chainInfo.chainId,
contract: (
await coreRelayer.registeredCoreRelayerContract(chain.chainId)
await coreRelayer.registeredCoreRelayerContract(chainInfo.chainId)
).toString(),
});
}

View File

@ -30,7 +30,7 @@ async function run() {
async function registerChainsCoreRelayer(chain: ChainInfo) {
console.log("registerChainsCoreRelayer " + chain.chainId);
const coreRelayer = getCoreRelayer(chain);
const coreRelayer = await getCoreRelayer(chain);
for (const targetChain of chains) {
await coreRelayer
.registerCoreRelayerContract(createRegisterChainVAA(targetChain))

View File

@ -51,7 +51,7 @@ async function upgradeCoreRelayer(
) {
console.log("upgradeCoreRelayer " + chain.chainId);
const coreRelayer = getCoreRelayer(chain);
const coreRelayer = await getCoreRelayer(chain);
await coreRelayer.submitContractUpgrade(
createCoreRelayerUpgradeVAA(chain, newImplementationAddress)

View File

@ -0,0 +1,30 @@
import {
init,
loadChains,
writeOutputFiles,
getMockIntegration,
Deployment,
getOperatingChains,
getMockIntegrationAddress,
} from "../helpers/env";
import { deployCreate2Factory } from "../helpers/deployments";
import { BigNumberish, BytesLike } from "ethers";
import { tryNativeToHexString } from "@certusone/wormhole-sdk";
import { wait } from "../helpers/utils";
const processName = "deployCreate2Factory";
init();
const chains = loadChains();
const operatingChains = getOperatingChains();
async function run() {
console.log("Start!");
const create2Factories = await Promise.all(
operatingChains.map(deployCreate2Factory)
);
writeOutputFiles({ create2Factories }, processName);
}
run().then(() => console.log("Done!"));

View File

@ -1,11 +1,10 @@
import { RelayProviderProxy__factory } from "../../../ethers-contracts/factories/RelayProviderProxy__factory";
import { RelayProviderSetup__factory } from "../../../ethers-contracts/factories/RelayProviderSetup__factory";
import { RelayProviderImplementation__factory } from "../../../ethers-contracts/factories/RelayProviderImplementation__factory";
import { MockRelayerIntegration__factory } from "../../../ethers-contracts/factories/MockRelayerIntegration__factory";
import { CoreRelayerProxy__factory } from "../../../ethers-contracts/factories/CoreRelayerProxy__factory";
import { CoreRelayerSetup__factory } from "../../../ethers-contracts/factories/CoreRelayerSetup__factory";
import { CoreRelayerImplementation__factory } from "../../../ethers-contracts/factories/CoreRelayerImplementation__factory";
import { CoreRelayerLibrary__factory } from "../../../ethers-contracts/factories/CoreRelayerLibrary__factory";
import { RelayProviderProxy__factory } from "../../../ethers-contracts";
import { RelayProviderSetup__factory } from "../../../ethers-contracts";
import { RelayProviderImplementation__factory } from "../../../ethers-contracts";
import { MockRelayerIntegration__factory } from "../../../ethers-contracts";
import { CoreRelayerProxy__factory } from "../../../ethers-contracts";
import { CoreRelayerSetup__factory } from "../../../ethers-contracts";
import { CoreRelayerImplementation__factory } from "../../../ethers-contracts";
import {
init,
@ -16,8 +15,19 @@ import {
Deployment,
getSigner,
getCoreRelayerAddress,
getCreate2Factory,
getCoreRelayer,
fetchSetupAddressCreate2,
} from "./env";
import { ethers } from "ethers";
import {
Create2Factory__factory,
ForwardWrapper__factory,
} from "../../../ethers-contracts";
import { wait } from "./utils";
export const setupContractSalt = Buffer.from("0xSetup");
export const proxyContractSalt = Buffer.from("0xGenericRelayer");
export async function deployRelayProviderImplementation(
chain: ChainInfo
@ -105,7 +115,7 @@ export async function deployMockIntegration(
);
const contract = await factory.deploy(
chain.wormholeAddress,
getCoreRelayerAddress(chain)
await getCoreRelayerAddress(chain)
);
return await contract.deployed().then((result) => {
console.log("Successfully deployed contract at " + result.address);
@ -113,78 +123,60 @@ export async function deployMockIntegration(
});
}
export async function deployCoreRelayerLibrary(
export async function deployCreate2Factory(
chain: ChainInfo
): Promise<Deployment> {
console.log("deployCreate2Factory " + chain.chainId);
const result = await new Create2Factory__factory(getSigner(chain))
.deploy()
.then(deployed);
console.log(`Successfully deployed contract at ${result.address}`);
return { address: result.address, chainId: chain.chainId };
}
export async function deployForwardWrapper(
chain: ChainInfo,
coreRelayerProxyAddress: string
): Promise<Deployment> {
console.log("deployCoreRelayerLibrary " + chain.chainId);
let signer = getSigner(chain);
const contractInterface = CoreRelayerLibrary__factory.createInterface();
const bytecode = CoreRelayerLibrary__factory.bytecode;
const factory = new ethers.ContractFactory(
contractInterface,
bytecode,
signer
);
const contract = await factory.deploy();
return await contract.deployed().then((result) => {
console.log("Successfully deployed contract at " + result.address);
return { address: result.address, chainId: chain.chainId };
});
const result = await new ForwardWrapper__factory(getSigner(chain))
.deploy(coreRelayerProxyAddress, chain.wormholeAddress)
.then(deployed);
console.log("Successfully deployed contract at " + result.address);
return { address: result.address, chainId: chain.chainId };
}
export async function deployCoreRelayerImplementation(
chain: ChainInfo,
coreRelayerLibraryAddress: string
forwardWrapperAddress: string
): Promise<Deployment> {
console.log("deployCoreRelayerImplementation " + chain.chainId);
const signer = getSigner(chain);
const contractInterface = CoreRelayerImplementation__factory.createInterface();
const bytecode: string = CoreRelayerImplementation__factory.bytecode;
/*
Linked libraries in EVM are contained in the bytecode and linked at compile time.
However, the linked address of the CoreRelayerLibrary is not known until deployment time,
So, rather that recompiling the contracts with a static link, we modify the bytecode directly
once we have the CoreRelayLibraryAddress.
*/
const bytecodeWithLibraryLink = link(
bytecode,
"CoreRelayerLibrary",
coreRelayerLibraryAddress
);
const result = await new CoreRelayerImplementation__factory(getSigner(chain))
.deploy(forwardWrapperAddress)
.then(deployed);
//@ts-ignore
const factory = new ethers.ContractFactory(
contractInterface,
bytecodeWithLibraryLink,
signer
);
const contract = await factory.deploy();
return await contract.deployed().then((result) => {
console.log("Successfully deployed contract at " + result.address);
return { address: result.address, chainId: chain.chainId };
});
console.log("Successfully deployed contract at " + result.address);
return { address: result.address, chainId: chain.chainId };
}
export async function deployCoreRelayerSetup(
chain: ChainInfo
): Promise<Deployment> {
console.log("deployCoreRelayerSetup " + chain.chainId);
const signer = getSigner(chain);
const contractInterface = CoreRelayerSetup__factory.createInterface();
const bytecode = CoreRelayerSetup__factory.bytecode;
//@ts-ignore
const factory = new ethers.ContractFactory(
contractInterface,
bytecode,
signer
);
const contract = await factory.deploy();
return await contract.deployed().then((result) => {
console.log("Successfully deployed contract at " + result.address);
return { address: result.address, chainId: chain.chainId };
});
const create2Factory = getCreate2Factory(chain);
const rx = await create2Factory
.create2(setupContractSalt, CoreRelayerSetup__factory.bytecode)
.then(wait);
const address = Create2Factory__factory.createInterface().parseLog(rx.logs[0])
.args[0];
console.log("Successfully deployed contract at " + address);
return { address, chainId: chain.chainId };
}
export async function deployCoreRelayerProxy(
chain: ChainInfo,
coreRelayerSetupAddress: string,
@ -193,40 +185,47 @@ export async function deployCoreRelayerProxy(
relayProviderProxyAddress: string
): Promise<Deployment> {
console.log("deployCoreRelayerProxy " + chain.chainId);
const signer = getSigner(chain);
const contractInterface = CoreRelayerProxy__factory.createInterface();
const bytecode = CoreRelayerProxy__factory.bytecode;
//@ts-ignore
const factory = new ethers.ContractFactory(
contractInterface,
bytecode,
signer
);
const create2Factory = getCreate2Factory(chain);
const expectedSetupAddr = await fetchSetupAddressCreate2(
chain,
create2Factory
);
if (coreRelayerSetupAddress !== expectedSetupAddr) {
throw new Error(
`coreRelayerSetupAddress different than expected. Expected: ${expectedSetupAddr} Actual: ${coreRelayerSetupAddress}`
);
}
// deploy proxy and point at setup contract
const data = new CoreRelayerProxy__factory().getDeployTransaction(
coreRelayerSetupAddress
).data!;
const rx = await create2Factory.create2(proxyContractSalt, data).then(wait);
// call setup
const governanceChainId = 1;
const governanceContract =
"0x0000000000000000000000000000000000000000000000000000000000000004";
let ABI = [
"function setup(address,uint16,address,address,uint16,bytes32,uint256)",
];
let iface = new ethers.utils.Interface(ABI);
let encodedData = iface.encodeFunctionData("setup", [
coreRelayerImplementationAddress,
chain.chainId,
wormholeAddress,
relayProviderProxyAddress,
governanceChainId,
governanceContract,
chain.evmNetworkId,
]);
const contract = await factory.deploy(coreRelayerSetupAddress, encodedData);
return await contract.deployed().then((result) => {
console.log("Successfully deployed contract at " + result.address);
return { address: result.address, chainId: chain.chainId };
});
const proxy = CoreRelayerSetup__factory.connect(
await getCoreRelayerAddress(chain),
getSigner(chain)
);
await proxy
.setup(
coreRelayerImplementationAddress,
chain.chainId,
wormholeAddress,
relayProviderProxyAddress,
governanceChainId,
governanceContract,
chain.evmNetworkId
)
.then(wait);
console.log("Successfully deployed contract at " + proxy.address);
return { address: proxy.address, chainId: chain.chainId };
}
function link(bytecode: string, libName: String, libAddress: string) {
//This doesn't handle the libName, because Forge embed a psuedonym into the bytecode, like
//__$a7dd444e34bd28bbe3641e0101a6826fa7$__
@ -235,3 +234,5 @@ function link(bytecode: string, libName: String, libAddress: string) {
let symbol = /__.*?__/g;
return bytecode.replace(symbol, libAddress.toLowerCase().substr(2));
}
const deployed = (x: ethers.Contract) => x.deployed();

View File

@ -1,14 +1,21 @@
import type { ChainId } from "@certusone/wormhole-sdk";
import { ChainId } from "@certusone/wormhole-sdk";
import { ethers, Signer } from "ethers";
import fs from "fs";
import { CoreRelayer } from "../../../ethers-contracts/CoreRelayer";
import { RelayProvider } from "../../../ethers-contracts/RelayProvider";
import { MockRelayerIntegration } from "../../../ethers-contracts/MockRelayerIntegration";
import { CoreRelayer } from "../../../ethers-contracts";
import { RelayProvider } from "../../../ethers-contracts";
import { MockRelayerIntegration } from "../../../ethers-contracts";
import { RelayProvider__factory } from "../../../ethers-contracts/factories/RelayProvider__factory";
import { CoreRelayer__factory } from "../../../ethers-contracts/factories/CoreRelayer__factory";
import { MockRelayerIntegration__factory } from "../../../ethers-contracts/factories/MockRelayerIntegration__factory";
import { RelayProvider__factory } from "../../../ethers-contracts";
import { CoreRelayer__factory } from "../../../ethers-contracts";
import { MockRelayerIntegration__factory } from "../../../ethers-contracts";
import {
CoreRelayerProxy__factory,
Create2Factory,
Create2Factory__factory,
} from "../../../ethers-contracts";
import { CoreRelayerSetup__factory } from "../../../ethers-contracts";
import { proxyContractSalt, setupContractSalt } from "./deployments";
export type ChainInfo = {
evmNetworkId: number;
@ -216,19 +223,61 @@ export function loadMockIntegrations(): Deployment[] {
}
}
export function loadCreate2Factories(): Deployment[] {
const contractsFile = fs.readFileSync(
`./ts-scripts/relayer/config/${env}/contracts.json`
);
if (!contractsFile) {
throw Error("Failed to find contracts file for this process!");
}
const contracts = JSON.parse(contractsFile.toString());
if (contracts.useLastRun || lastRunOverride) {
const lastRunFile = fs.readFileSync(
`./ts-scripts/relayer/output/${env}/deployCreate2Factory/lastrun.json`
);
if (!lastRunFile) {
throw Error(
"Failed to find last run file for the deployCreate2Factory process!"
);
}
const lastRun = JSON.parse(lastRunFile.toString());
return lastRun.create2Factories;
} else {
return contracts.create2Factories;
}
}
//TODO load these keys more intelligently,
//potentially from devnet-consts
//potentially from devnet-consts.
//Also, make sure the signers are correctly ordered by index,
//As the index gets encoded into the signature.
export function loadGuardianKeys(): string[] {
const output = [];
const NUM_GUARDIANS = get_env_var("NUM_GUARDIANS");
const guardianKey = get_env_var("GUARDIAN_KEY");
const guardianKey2 = get_env_var("GUARDIAN_KEY2");
let numGuardians: number = 0;
console.log("NUM_GUARDIANS variable : " + NUM_GUARDIANS);
if (!NUM_GUARDIANS) {
numGuardians = 1;
} else {
numGuardians = parseInt(NUM_GUARDIANS);
}
if (!guardianKey) {
throw Error("Failed to find guardian key for this process!");
}
if(guardianKey2) {
output.push(guardianKey2);
}
output.push(guardianKey);
if (numGuardians >= 2) {
if (!guardianKey2) {
throw Error("Failed to find guardian key 2 for this process!");
}
output.push(guardianKey2);
}
return output;
}
@ -248,7 +297,7 @@ export function writeOutputFiles(output: any, processName: string) {
);
}
export function getSigner(chain: ChainInfo): Signer {
export function getSigner(chain: ChainInfo): ethers.Wallet {
let provider = getProvider(chain);
let signer = new ethers.Wallet(loadPrivateKey(), provider);
return signer;
@ -289,28 +338,73 @@ export function getRelayProvider(
return contract;
}
export function getCoreRelayerAddress(chain: ChainInfo): string {
const thisChainsRelayer = loadCoreRelayers().find(
(x: any) => x.chainId == chain.chainId
)?.address;
if (!thisChainsRelayer) {
throw new Error(
"Failed to find a CoreRelayer contract address on chain " + chain.chainId
);
}
return thisChainsRelayer;
export function fetchSetupAddressCreate2(
chain: ChainInfo,
create2Factory = getCreate2Factory(chain)
): Promise<string> {
const signer = getSigner(chain).address;
return create2Factory.computeAddress(
signer,
setupContractSalt,
ethers.utils.solidityKeccak256(
["bytes"],
[CoreRelayerSetup__factory.bytecode]
)
);
}
export function getCoreRelayer(
const coreRelayerAddressesCache: Partial<Record<ChainId, string>> = {};
export async function getCoreRelayerAddress(chain: ChainInfo): Promise<string> {
const contractsFile = fs.readFileSync(
`./ts-scripts/relayer/config/${env}/contracts.json`
);
if (!contractsFile) {
throw Error("Failed to find contracts file for this process!");
}
const contracts = JSON.parse(contractsFile.toString());
//If useLastRun is false, then we want to bypass the calculations and just use what the contracts file says.
if (!contracts.useLastRun && !lastRunOverride) {
const thisChainsRelayer = loadCoreRelayers().find(
(x: any) => x.chainId == chain.chainId
)?.address;
if (thisChainsRelayer) {
return thisChainsRelayer;
} else {
throw Error(
"Failed to find a CoreRelayer contract address on chain " +
chain.chainId
);
}
}
if (!coreRelayerAddressesCache[chain.chainId]) {
const create2Factory = getCreate2Factory(chain);
const signer = getSigner(chain).address;
const setupAddr = await fetchSetupAddressCreate2(chain, create2Factory);
const data = new CoreRelayerProxy__factory().getDeployTransaction(setupAddr)
.data!;
coreRelayerAddressesCache[
chain.chainId
] = await create2Factory.computeAddress(
signer,
proxyContractSalt,
ethers.utils.solidityKeccak256(["bytes"], [data])
);
}
return coreRelayerAddressesCache[chain.chainId]!;
}
export async function getCoreRelayer(
chain: ChainInfo,
provider?: ethers.providers.StaticJsonRpcProvider
): CoreRelayer {
const thisChainsRelayer = getCoreRelayerAddress(chain);
const contract = CoreRelayer__factory.connect(
): Promise<CoreRelayer> {
const thisChainsRelayer = await getCoreRelayerAddress(chain);
return CoreRelayer__factory.connect(
thisChainsRelayer,
provider || getSigner(chain)
);
return contract;
}
export function getMockIntegrationAddress(chain: ChainInfo): string {
@ -334,3 +428,22 @@ export function getMockIntegration(chain: ChainInfo): MockRelayerIntegration {
);
return contract;
}
export function getCreate2FactoryAddress(chain: ChainInfo): string {
const address = loadCreate2Factories().find(
(x: any) => x.chainId == chain.chainId
)?.address;
if (!address) {
throw new Error(
"Failed to find a create2Factory contract address on chain " +
chain.chainId
);
}
return address;
}
export const getCreate2Factory = (chain: ChainInfo): Create2Factory =>
Create2Factory__factory.connect(
getCreate2FactoryAddress(chain),
getSigner(chain)
);

View File

@ -61,8 +61,11 @@ export function createDefaultRelayProviderVAA(chain: ChainInfo) {
return encodeAndSignGovernancePayload(payload);
}
export function createRegisterChainVAA(chain: ChainInfo): string {
const coreRelayerAddress = getCoreRelayerAddress(chain);
export async function createRegisterChainVAA(
chain: ChainInfo
): Promise<string> {
const coreRelayerAddress = await getCoreRelayerAddress(chain);
console.log(`Registering ${coreRelayerAddress} on chain ${chain.chainId}`);
// bytes32 module;
// uint8 action;
@ -105,18 +108,18 @@ export function encodeAndSignGovernancePayload(payload: string): string {
const hash = doubleKeccak256(encodedVAABody);
const pks = loadGuardianKeys();
const signers = loadGuardianKeys();
let signatures = "";
for (let pk of pks) {
for (const i in signers) {
// sign the hash
const ec = new elliptic.ec("secp256k1");
const key = ec.keyFromPrivate(pk);
const key = ec.keyFromPrivate(signers[i]);
const signature = key.sign(hash.substring(2), { canonical: true });
// pack the signatures
const packSig = [
ethers.utils.solidityPack(["uint8"], [0]).substring(2),
ethers.utils.solidityPack(["uint8"], [i]).substring(2),
zeroPadBytes(signature.r.toString(16), 32),
zeroPadBytes(signature.s.toString(16), 32),
ethers.utils
@ -131,7 +134,7 @@ export function encodeAndSignGovernancePayload(payload: string): string {
ethers.utils
.solidityPack(["uint32"], [loadGuardianSetIndex()])
.substring(2), // guardianSetIndex
ethers.utils.solidityPack(["uint8"], [pks.length]).substring(2), // number of signers
ethers.utils.solidityPack(["uint8"], [signers.length]).substring(2), // number of signers
signatures,
encodedVAABody.substring(2),
].join("");

View File

@ -8,9 +8,11 @@ import {
getMockIntegrationAddress,
getProvider,
getRelayProvider,
getSigner,
} from "../helpers/env";
import * as grpcWebNodeHttpTransport from "@improbable-eng/grpc-web-node-http-transport";
import { ethers } from "ethers";
import { RelayProvider__factory } from "../../../ethers-contracts";
export async function sendMessage(
sourceChain: ChainInfo,
@ -22,7 +24,7 @@ export async function sendMessage(
`Sending message from chain ${sourceChain.chainId} to ${targetChain.chainId}...`
);
const sourceRelayer = getCoreRelayer(sourceChain);
const sourceRelayer = await getCoreRelayer(sourceChain);
const sourceProvider = await sourceRelayer.getDefaultRelayProvider();
const relayQuote = await (

View File

@ -17,7 +17,7 @@ async function run(
sourceTxHash: string,
deliveryVAASequence: number,
) {
const coreRelayer = getCoreRelayer(sourceChain)
const coreRelayer = await getCoreRelayer(sourceChain)
const relayProvider = await coreRelayer.getDefaultRelayProvider()
const relayQuote = await (

File diff suppressed because one or more lines are too long

View File

@ -42,7 +42,7 @@ async function run() {
async function configureChainsRelayProvider(chain: ChainInfo) {
console.log("about to perform RelayProvider configuration for chain " + chain.chainId);
const relayProvider = getRelayProvider(chain);
const coreRelayer = getCoreRelayerAddress(chain);
const coreRelayer = await getCoreRelayerAddress(chain);
const thisChainsConfigInfo = config.addresses.find(
(x: any) => x.chainId == chain.chainId

View File

@ -1,8 +1,11 @@
npx ts-node ./ts-scripts/relayer/config/checkNetworks.ts --last-run=true \
&& npx ts-node ./ts-scripts/relayer/relayProvider/deployRelayProvider.ts \
npx ts-node ./ts-scripts/relayer/config/checkNetworks.ts --set-last-run \
&& npx ts-node ./ts-scripts/relayer/create2Factory/deployCreate2Factory.ts \
&& npx ts-node ./ts-scripts/relayer/coreRelayer/deployCoreRelayer.ts \
&& npx ts-node ./ts-scripts/relayer/coreRelayer/registerChainsCoreRelayerSelfSign.ts \
&& npx ts-node ./ts-scripts/relayer/relayProvider/deployRelayProvider.ts \
&& npx ts-node ./ts-scripts/relayer/relayProvider/configureRelayProvider.ts \
&& npx ts-node ./ts-scripts/relayer/coreRelayer/registerChainsCoreRelayerSelfSign.ts \
&& npx ts-node ./ts-scripts/relayer/mockIntegration/deployMockIntegration.ts \
&& npx ts-node ./ts-scripts/relayer/mockIntegration/messageTest.ts \
&& npx ts-node ./ts-scripts/relayer/config/syncContractsJson.ts
&& npx ts-node ./ts-scripts/relayer/config/syncContractsJson.ts \
&& npx ts-node ./ts-scripts/relayer/mockIntegration/messageTest.ts
# put this as 2nd script if not deployed aleady

View File

@ -1,6 +1,7 @@
echo "deploying generic relayer contracts" \
npx ts-node ./ts-scripts/relayer/relayProvider/deployRelayProvider.ts \
npx ts-node ./ts-scripts/relayer/create2Factory/deployCreate2Factory.ts \
&& npx ts-node ./ts-scripts/relayer/relayProvider/deployRelayProvider.ts \
&& npx ts-node ./ts-scripts/relayer/coreRelayer/deployCoreRelayer.ts \
&& npx ts-node ./ts-scripts/relayer/mockIntegration/deployMockIntegration.ts \
&& npx ts-node ./ts-scripts/relayer/coreRelayer/registerChainsCoreRelayerSelfSign.ts \
&& npx ts-node ./ts-scripts/relayer/relayProvider/configureRelayProvider.ts \
&& npx ts-node ./ts-scripts/relayer/relayProvider/configureRelayProvider.ts \
&& npx ts-node ./ts-scripts/relayer/mockIntegration/deployMockIntegration.ts \

5
remappings.txt Normal file
View File

@ -0,0 +1,5 @@
@openzeppelin/=ethereum/node_modules/@openzeppelin/
@solidity-parser/=ethereum/node_modules/@solidity-parser/
ds-test/=ethereum/lib/forge-std/lib/ds-test/src/
forge-std/=ethereum/lib/forge-std/src/
truffle/=ethereum/node_modules/truffle/