Skip to content

Commit

Permalink
Deployment scripts for BeefyClient & Gateway (#1190)
Browse files Browse the repository at this point in the history
  • Loading branch information
vgeddes authored May 23, 2024
1 parent dea40b4 commit ec27c1a
Show file tree
Hide file tree
Showing 12 changed files with 269 additions and 35 deletions.
44 changes: 44 additions & 0 deletions contracts/scripts/DeployBeefyClient.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.23;

import {Script} from "forge-std/Script.sol";
import {BeefyClient} from "../src/BeefyClient.sol";

contract DeployBeefyClient is Script {
struct Config {
uint64 startBlock;
BeefyClient.ValidatorSet current;
BeefyClient.ValidatorSet next;
uint256 randaoCommitDelay;
uint256 randaoCommitExpiration;
uint256 minimumSignatures;
}

function readConfig() internal pure returns (Config memory config) {
// Checkpoint generated at block 20733663 using the `./beefy-checkpoint.js` script in Polkadot-JS.
// Block 20733663 is significant as that was when our bridge was initialized on BridgeHub.
config = Config({
startBlock: 20733663,
current: BeefyClient.ValidatorSet({id: 496, length: 297, root: 0xdd04a3a0a4a19180bdae78ecc0c089491d22f5b65b685199d877f20b7fc76434}),
next: BeefyClient.ValidatorSet({id: 497, length: 297, root: 0xdd04a3a0a4a19180bdae78ecc0c089491d22f5b65b685199d877f20b7fc76434}),
randaoCommitDelay: 128,
randaoCommitExpiration: 24,
minimumSignatures: 17
});
}

function run() public {
vm.startBroadcast();
Config memory config = readConfig();

new BeefyClient(
config.randaoCommitDelay,
config.randaoCommitExpiration,
config.minimumSignatures,
config.startBlock,
config.current,
config.next
);
}
}
3 changes: 2 additions & 1 deletion contracts/scripts/DeployLocal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ contract DeployLocal is Script {
assetHubCreateAssetFee: uint128(vm.envUint("CREATE_ASSET_FEE")),
assetHubReserveTransferFee: uint128(vm.envUint("RESERVE_TRANSFER_FEE")),
exchangeRate: ud60x18(vm.envUint("EXCHANGE_RATE")),
multiplier: ud60x18(vm.envUint("FEE_MULTIPLIER"))
multiplier: ud60x18(vm.envUint("FEE_MULTIPLIER")),
rescueOperator: address(0)
});

GatewayProxy gateway = new GatewayProxy(address(gatewayLogic), abi.encode(config));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {ParaID} from "../src//Types.sol";
import {Script} from "forge-std/Script.sol";
import {stdJson} from "forge-std/StdJson.sol";

contract DeployGatewayLogic is Script {
contract DeployLocalGatewayLogic is Script {
using stdJson for string;

function setUp() public {}
Expand Down
91 changes: 91 additions & 0 deletions contracts/scripts/UpgradeShell.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.23;

import {WETH9} from "canonical-weth/WETH9.sol";
import {Script} from "forge-std/Script.sol";
import {BeefyClient} from "../src/BeefyClient.sol";

import {IGateway} from "../src/interfaces/IGateway.sol";
import {IShell} from "../src/interfaces/IShell.sol";
import {GatewayProxy} from "../src/GatewayProxy.sol";
import {Gateway} from "../src/Gateway.sol";
import {MockGatewayV2} from "../test/mocks/MockGatewayV2.sol";
import {Agent} from "../src/Agent.sol";
import {AgentExecutor} from "../src/AgentExecutor.sol";
import {ChannelID, ParaID, OperatingMode} from "../src/Types.sol";
import {SafeNativeTransfer} from "../src/utils/SafeTransfer.sol";
import {stdJson} from "forge-std/StdJson.sol";
import {UD60x18, ud60x18} from "prb/math/src/UD60x18.sol";

function mDot(uint32 value) pure returns (uint128) {
// 1 mDOT = 0.001 DOT
return value * (10 ** 7);
}

function dot(uint32 value) pure returns (uint128) {
return value * (10 ** 10);
}

contract UpgradeShell is Script {
using SafeNativeTransfer for address payable;
using stdJson for string;

struct Config {
address gatewayProxy;
address beefyClient;
ParaID bridgeHubParaID;
bytes32 bridgeHubAgentID;
uint8 foreignTokenDecimals;
uint128 maxDestinationFee;
Gateway.Config initializerParams;
}

function readConfig() internal pure returns (Config memory config) {
config = Config({
gatewayProxy: 0x27ca963C279c93801941e1eB8799c23f407d68e7,
beefyClient: address(0),
bridgeHubParaID: ParaID.wrap(1002),
bridgeHubAgentID: 0x03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314,
foreignTokenDecimals: 10,
maxDestinationFee: dot(2),
initializerParams: Gateway.Config({
mode: OperatingMode.Normal,
deliveryCost: mDot(100), // 0.1 DOT
registerTokenFee: 0.002 ether,
assetHubParaID: ParaID.wrap(1000),
assetHubAgentID: 0x81c5ab2571199e3188135178f3c2c8e2d268be1313d029b30f534fa579b69b79,
assetHubCreateAssetFee: mDot(100), // 0.1 DOT
assetHubReserveTransferFee: mDot(100), // 0.1 DOT
exchangeRate: ud60x18(0.0024e18),
multiplier: ud60x18(1.33e18),
rescueOperator: 0x4B8a782D4F03ffcB7CE1e95C5cfe5BFCb2C8e967
})
});
}

function run() public {
vm.startBroadcast();

Config memory config = readConfig();

// AgentExecutor
AgentExecutor executor = new AgentExecutor();

// Gateway implementation
Gateway gatewayLogic = new Gateway(
config.beefyClient,
address(executor),
config.bridgeHubParaID,
config.bridgeHubAgentID,
config.foreignTokenDecimals,
config.maxDestinationFee
);

IShell shell = IShell(config.gatewayProxy);

shell.upgrade(address(gatewayLogic), address(gatewayLogic).codehash, abi.encode(config.initializerParams));

vm.stopBroadcast();
}
}
25 changes: 25 additions & 0 deletions contracts/scripts/beefy-checkpoint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Polkadot-JS script to generate a BEEFY checkpoint

let beefyBlock = 20733663;
let blockHash = await api.rpc.chain.getBlockHash(beefyBlock);
let apiAtBlock = await api.at(blockHash);

let authorities = await apiAtBlock.query.beefyMmrLeaf.beefyAuthorities();
let nextAuthorities =
await apiAtBlock.query.beefyMmrLeaf.beefyNextAuthorities();

let beefyCheckpoint = {
startBlock: beefyBlock,
current: {
id: authorities.id.toNumber(),
root: authorities.keysetCommitment.toHex(),
length: authorities.len.toNumber(),
},
next: {
id: nextAuthorities.id.toNumber(),
root: nextAuthorities.keysetCommitment.toHex(),
length: nextAuthorities.len.toNumber(),
},
};

console.log(JSON.stringify(beefyCheckpoint, null, 2));
20 changes: 0 additions & 20 deletions contracts/scripts/minsigs.py

This file was deleted.

33 changes: 32 additions & 1 deletion contracts/src/Gateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
import {CoreStorage} from "./storage/CoreStorage.sol";
import {PricingStorage} from "./storage/PricingStorage.sol";
import {AssetsStorage} from "./storage/AssetsStorage.sol";
import {OperatorStorage} from "./storage/OperatorStorage.sol";

import {UD60x18, ud60x18, convert} from "prb/math/src/UD60x18.sol";

Expand Down Expand Up @@ -416,7 +417,9 @@ contract Gateway is IGateway, IInitializable, IUpgradable {
uint128 amount
) external payable {
_submitOutbound(
Assets.sendToken(token, msg.sender, destinationChain, destinationAddress, destinationFee, MAX_DESTINATION_FEE, amount)
Assets.sendToken(
token, msg.sender, destinationChain, destinationAddress, destinationFee, MAX_DESTINATION_FEE, amount
)
);
}

Expand Down Expand Up @@ -562,6 +565,8 @@ contract Gateway is IGateway, IInitializable, IUpgradable {
uint256 registerTokenFee;
/// @dev Fee multiplier
UD60x18 multiplier;
/// @dev Optional rescueOperator
address rescueOperator;
}

/// @dev Initialize storage in the gateway
Expand Down Expand Up @@ -612,5 +617,31 @@ contract Gateway is IGateway, IInitializable, IUpgradable {
assets.registerTokenFee = config.registerTokenFee;
assets.assetHubCreateAssetFee = config.assetHubCreateAssetFee;
assets.assetHubReserveTransferFee = config.assetHubReserveTransferFee;

// Initialize operator storage
OperatorStorage.Layout storage operatorStorage = OperatorStorage.layout();
operatorStorage.operator = config.rescueOperator;
}

/// @dev Temporary rescue ability for the initial bootstrapping phase of the bridge
function rescue(address impl, bytes32 implCodeHash, bytes calldata initializerParams) external {
OperatorStorage.Layout storage operatorStorage = OperatorStorage.layout();
if (msg.sender != operatorStorage.operator) {
revert Unauthorized();
}
Upgrade.upgrade(impl, implCodeHash, initializerParams);
}

function dropRescueAbility() external {
OperatorStorage.Layout storage operatorStorage = OperatorStorage.layout();
if (msg.sender != operatorStorage.operator) {
revert Unauthorized();
}
operatorStorage.operator = address(0);
}

function rescueOperator() external view returns (address) {
OperatorStorage.Layout storage operatorStorage = OperatorStorage.layout();
return operatorStorage.operator;
}
}
18 changes: 18 additions & 0 deletions contracts/src/storage/OperatorStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.23;

library OperatorStorage {
struct Layout {
address operator;
}

bytes32 internal constant SLOT = keccak256("org.snowbridge.storage.operator");

function layout() internal pure returns (Layout storage $) {
bytes32 slot = SLOT;
assembly {
$.slot := slot
}
}
}
62 changes: 53 additions & 9 deletions contracts/test/Gateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {SubstrateTypes} from "./../src/SubstrateTypes.sol";
import {MultiAddress} from "../src/MultiAddress.sol";
import {Channel, InboundMessage, OperatingMode, ParaID, Command, ChannelID, MultiAddress} from "../src/Types.sol";


import {NativeTransferFailed} from "../src/utils/SafeTransfer.sol";
import {PricingStorage} from "../src/storage/PricingStorage.sol";

Expand Down Expand Up @@ -99,12 +98,7 @@ contract GatewayTest is Test {
function setUp() public {
AgentExecutor executor = new AgentExecutor();
gatewayLogic = new MockGateway(
address(0),
address(executor),
bridgeHubParaID,
bridgeHubAgentID,
foreignTokenDecimals,
maxDestinationFee
address(0), address(executor), bridgeHubParaID, bridgeHubAgentID, foreignTokenDecimals, maxDestinationFee
);
Gateway.Config memory config = Gateway.Config({
mode: OperatingMode.Normal,
Expand All @@ -115,7 +109,8 @@ contract GatewayTest is Test {
assetHubCreateAssetFee: createTokenFee,
assetHubReserveTransferFee: sendTokenFee,
exchangeRate: exchangeRate,
multiplier: multiplier
multiplier: multiplier,
rescueOperator: 0x4B8a782D4F03ffcB7CE1e95C5cfe5BFCb2C8e967
});
gateway = new GatewayProxy(address(gatewayLogic), abi.encode(config));
MockGateway(address(gateway)).setCommitmentsAreVerified(true);
Expand Down Expand Up @@ -857,6 +852,55 @@ contract GatewayTest is Test {
IGateway(address(gateway)).quoteSendTokenFee(address(token), destPara, maxDestinationFee + 1);

vm.expectRevert(Assets.InvalidDestinationFee.selector);
IGateway(address(gateway)).sendToken{value: fee}(address(token), destPara, recipientAddress32, maxDestinationFee + 1, 1);
IGateway(address(gateway)).sendToken{value: fee}(
address(token), destPara, recipientAddress32, maxDestinationFee + 1, 1
);
}

function testRescuebyTrustedOperator() public {
// Upgrade to this new logic contract
MockGatewayV2 newLogic = new MockGatewayV2();

address impl = address(newLogic);
bytes32 implCodeHash = address(newLogic).codehash;
bytes memory initParams = abi.encode(42);

// Expect the gateway to emit `Upgraded`
vm.expectEmit(true, false, false, false);
emit IUpgradable.Upgraded(address(newLogic));

hoax(0x4B8a782D4F03ffcB7CE1e95C5cfe5BFCb2C8e967);
Gateway(address(gateway)).rescue(impl, implCodeHash, initParams);

// Verify that the MockGatewayV2.initialize was called
assertEq(MockGatewayV2(address(gateway)).getValue(), 42);
}

function testRescuebyPublicFails() public {
// Upgrade to this new logic contract
MockGatewayV2 newLogic = new MockGatewayV2();

address impl = address(newLogic);
bytes32 implCodeHash = address(newLogic).codehash;
bytes memory initParams = abi.encode(42);

vm.expectRevert(Gateway.Unauthorized.selector);
Gateway(address(gateway)).rescue(impl, implCodeHash, initParams);
}

function testDropRescueAbility() public {
// Upgrade to this new logic contract
MockGatewayV2 newLogic = new MockGatewayV2();

address impl = address(newLogic);
bytes32 implCodeHash = address(newLogic).codehash;
bytes memory initParams = abi.encode(42);

hoax(0x4B8a782D4F03ffcB7CE1e95C5cfe5BFCb2C8e967);
Gateway(address(gateway)).dropRescueAbility();

vm.expectRevert(Gateway.Unauthorized.selector);
hoax(0x4B8a782D4F03ffcB7CE1e95C5cfe5BFCb2C8e967);
Gateway(address(gateway)).rescue(impl, implCodeHash, initParams);
}
}
2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"private": true,
"engines": {
"node": "^20 || ^18",
"pnpm": "^8"
"pnpm": ">=8"
},
"scripts": {
"preinstall": "npx only-allow pnpm",
Expand Down
2 changes: 1 addition & 1 deletion web/packages/test-helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"format": "prettier src --write"
},
"devDependencies": {
"@types/node": "^18.16.8",
"@typescript-eslint/eslint-plugin": "^5.42.0",
"@typescript-eslint/parser": "^5.42.0",
"eslint": "^8.26.0",
Expand All @@ -38,7 +39,6 @@
"@typechain/ethers-v5": "^10.1.1",
"@types/keccak": "^3.0.1",
"@types/lodash": "^4.14.186",
"@types/node": "^18.13.0",
"@types/secp256k1": "^4.0.3",
"@types/seedrandom": "^3.0.2",
"bitfield": "^4.1.0",
Expand Down
Loading

0 comments on commit ec27c1a

Please sign in to comment.