Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions app/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"

"github.com/tellor-io/layer/app/upgrades"
v_6_1_3 "github.com/tellor-io/layer/app/upgrades/v6.1.3"
v_6_1_4 "github.com/tellor-io/layer/app/upgrades/v6.1.4"

upgradetypes "cosmossdk.io/x/upgrade/types"
)
Expand All @@ -13,22 +13,25 @@ var (
// `Upgrades` defines the upgrade handlers and store loaders for the application.
// New upgrades should be added to this slice after they are implemented.
Upgrades = []*upgrades.Upgrade{
&v_6_1_3.Upgrade,
&v_6_1_4.Upgrade,
}
Forks = []upgrades.Fork{}
)

// setupUpgradeHandlers registers the upgrade handlers to perform custom upgrade
// logic and state migrations for software upgrades.
func (app *App) setupUpgradeHandlers() {
if app.UpgradeKeeper.HasHandler(v_6_1_3.UpgradeName) {
panic(fmt.Sprintf("Cannot register duplicate upgrade handler '%s'", v_6_1_3.UpgradeName))
if app.UpgradeKeeper.HasHandler(v_6_1_4.UpgradeName) {
panic(fmt.Sprintf("Cannot register duplicate upgrade handler '%s'", v_6_1_4.UpgradeName))
}
app.UpgradeKeeper.SetUpgradeHandler(
v_6_1_3.UpgradeName,
v_6_1_3.CreateUpgradeHandler(
v_6_1_4.UpgradeName,
v_6_1_4.CreateUpgradeHandler(
app.ModuleManager(),
app.configurator,
app.OracleKeeper,
app.BridgeKeeper,
app.RegistryKeeper,
),
)
}
Expand Down
16 changes: 16 additions & 0 deletions app/upgrades/v6.1.4/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package v6_1_4

import (
"github.com/tellor-io/layer/app/upgrades"

store "cosmossdk.io/store/types"
)

const (
UpgradeName = "v6.1.4"
)

var Upgrade = upgrades.Upgrade{
UpgradeName: UpgradeName,
StoreUpgrades: store.StoreUpgrades{},
}
107 changes: 107 additions & 0 deletions app/upgrades/v6.1.4/upgrade.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package v6_1_4

import (
"context"
"fmt"
"os"

"github.com/ethereum/go-ethereum/common"
bridgekeeper "github.com/tellor-io/layer/x/bridge/keeper"
bridgetypes "github.com/tellor-io/layer/x/bridge/types"
oraclekeeper "github.com/tellor-io/layer/x/oracle/keeper"
registrykeeper "github.com/tellor-io/layer/x/registry/keeper"
registrytypes "github.com/tellor-io/layer/x/registry/types"

"cosmossdk.io/math"
upgradetypes "cosmossdk.io/x/upgrade/types"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)

/*
Upgrade to v6.1.4 includes:
- Deprecation of TRBBridge query type in favor of TRBBridgeV2
- Updates to oracle keeper to handle TRBBridgeV2 query type and prevent usage of TRBBridge
*/
// TODO: change these to real addresses
const (
MainnetChainID = "tellor1"
MainnetTokenBridgeV2 = "0x0000000000000000000000000000000000000000"
TestnetChainID = "layertest-4"
TestnetTokenBridgeV2 = "0x0000000000000000000000000000000000000000"
)

func CreateUpgradeHandler(
mm *module.Manager,
configurator module.Configurator,
oracleKeeper oraclekeeper.Keeper,
bridgeKeeper bridgekeeper.Keeper,
registryKeeper registrykeeper.Keeper,
) upgradetypes.UpgradeHandler {
trbbridgeV2 := registrytypes.DataSpec{
DocumentHash: "",
ResponseValueType: "address, string, uint256, uint256",
AbiComponents: []*registrytypes.ABIComponent{
{
Name: "toLayer",
FieldType: "bool",
NestedComponent: []*registrytypes.ABIComponent{},
},
{
Name: "depositId",
FieldType: "uint256",
NestedComponent: []*registrytypes.ABIComponent{},
},
},
AggregationMethod: "weighted-mode",
Registrar: "genesis",
ReportBlockWindow: 2000,
QueryType: "trbbridgev2",
}

return func(ctx context.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
err := registryKeeper.SetDataSpec(sdkCtx, "trbbridgev2", trbbridgeV2)
if err != nil {
return nil, err
}

tokenBridgeV2Address, err := resolveTokenBridgeV2Address(sdkCtx.ChainID())
if err != nil {
return nil, err
}

withdrawalId := uint64(1_000_000_000_000)
recipient := common.HexToAddress(tokenBridgeV2Address).Bytes()
sender := authtypes.NewModuleAddress(bridgetypes.ModuleName)
amount := sdk.NewCoin("loya", math.NewInt(2_800_000_000_000)) // 2.8 million TRB - 1 TRB = 1_000_000 loya

aggregate, queryData, err := bridgeKeeper.CreateWithdrawalAggregate(ctx, "TRBBridge", amount, sender, recipient, withdrawalId)
if err != nil {
return nil, err
}
err = oracleKeeper.SetAggregate(sdkCtx, aggregate, queryData, "TRBBridge-withdraw")
if err != nil {
return nil, err
}
sdkCtx.Logger().Info(fmt.Sprintf("Running %s Upgrade...", UpgradeName))
return mm.RunMigrations(ctx, configurator, vm)
}
}

func resolveTokenBridgeV2Address(chainID string) (string, error) {
switch chainID {
case MainnetChainID:
return MainnetTokenBridgeV2, nil
case TestnetChainID:
return TestnetTokenBridgeV2, nil
default:
addr := os.Getenv("TOKEN_BRIDGE_V2_ADDRESS")
if addr == "" {
return "", fmt.Errorf("TOKEN_BRIDGE_V2_ADDRESS env var not set for chain-id %s", chainID)
}
return addr, nil
}
}
4 changes: 3 additions & 1 deletion e2e/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ import (
const (
haltHeightDelta = 12 // will propose upgrade this many blocks in the future
blocksAfterUpgrade = 12
testTokenBridgeV2 = "0x0000000000000000000000000000000000000000"
)

func TestLayerUpgrade(t *testing.T) {
ChainUpgradeTest(t, "layer", "layer", "local", "v6.1.3")
ChainUpgradeTest(t, "layer", "layer", "local", "v6.1.4")
}

func ChainUpgradeTest(t *testing.T, chainName, upgradeContainerRepo, upgradeVersion, upgradeName string) {
Expand Down Expand Up @@ -78,6 +79,7 @@ func ChainUpgradeTest(t *testing.T, chainName, upgradeContainerRepo, upgradeVers
EncodingConfig: e2e.LayerEncoding(),
ModifyGenesis: cosmos.ModifyGenesis(modifyGenesis),
AdditionalStartArgs: []string{"--key-name", "validator"},
Env: []string{"TOKEN_BRIDGE_V2_ADDRESS=" + testTokenBridgeV2},
},
},
})
Expand Down
28 changes: 28 additions & 0 deletions evm/contracts/testing/TestTokenBridgeV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import { TokenBridgeV2 } from "../token-bridge/TokenBridgeV2.sol";

/// @title TokenBridgeV2
/// @dev allows us to test internal limit refreshes externally
contract TestTokenBridgeV2 is TokenBridgeV2 {
constructor(
address _token,
address _blobstream,
address _tellorFlex,
address _mainGuardian,
address _subGuardian,
uint256 _defaultRoleUpdateDelay,
uint256 _pausePeriod
) TokenBridgeV2(_token, _blobstream, _tellorFlex, _mainGuardian, _subGuardian, _defaultRoleUpdateDelay, _pausePeriod) {}

/// @notice refreshes the deposit limit every 12 hours so no one can spam layer with new tokens
function refreshDepositLimit() external returns (uint256) {
return _refreshDepositLimit();
}

/// @notice refreshes the withdraw limit every 12 hours so no one can spam layer with new tokens
function refreshWithdrawLimit() external returns (uint256) {
return _refreshWithdrawLimit();
}
}
83 changes: 83 additions & 0 deletions evm/contracts/token-bridge/RoleManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

/// @title RoleManager
/// @notice Role manager for the TokenBridgeV2
contract RoleManager {
/*Storage*/
mapping(bytes32 => RoleInfo) public roles;
mapping(bytes32 => RoleUpdate) public roleUpdateProposals;

struct RoleInfo {
address roleAddress;
uint256 roleUpdateDelay;
}

struct RoleUpdate {
address newAddress;
uint256 newUpdateDelay;
uint256 proposalTime;
}

/*Events*/
event RoleUpdateProposed(bytes32 _role, address _newAddress, uint256 _newUpdateDelay);
event RoleUpdateAccepted(bytes32 _role, address _newAddress);
event RoleUpdateRejected(bytes32 _role);

/*Functions*/
/// @notice constructor
/// @param _guardian the address of the guardian
/// @param _guardianUpdateDelay the delay before the guardian update can be accepted
constructor(address _guardian, uint256 _guardianUpdateDelay) {
roles[keccak256("MAIN_GUARDIAN")] = RoleInfo({
roleAddress: _guardian,
roleUpdateDelay: _guardianUpdateDelay
});
}

/// @notice proposes a role update
/// @param _role the role to update
/// @param _newAddress the new address for the role
/// @param _newUpdateDelay the delay before the role update can be accepted
function proposeRoleUpdate(bytes32 _role, address _newAddress, uint256 _newUpdateDelay) external {
_roleRestricted(keccak256("MAIN_GUARDIAN"));
RoleUpdate storage _roleUpdate = roleUpdateProposals[_role];
require(_roleUpdate.newAddress == address(0), "RoleManager: Role update already proposed");
require(_newAddress != address(0), "RoleManager: New address cannot be the zero address");
_roleUpdate.newAddress = _newAddress;
_roleUpdate.newUpdateDelay = _newUpdateDelay;
_roleUpdate.proposalTime = block.timestamp;
emit RoleUpdateProposed(_role, _newAddress, _newUpdateDelay);
}

/// @notice accepts a role update
/// @param _role the role to update
function acceptRoleUpdate(bytes32 _role) external {
_roleRestricted(keccak256("MAIN_GUARDIAN"));
RoleUpdate storage _roleUpdateProposal = roleUpdateProposals[_role];
require(_roleUpdateProposal.newAddress != address(0), "RoleManager: Role update not proposed");
RoleInfo storage _roleInfo = roles[_role];
require(block.timestamp - _roleUpdateProposal.proposalTime > _roleInfo.roleUpdateDelay, "RoleManager: Role update delay not passed");
_roleInfo.roleAddress = _roleUpdateProposal.newAddress;
_roleInfo.roleUpdateDelay = _roleUpdateProposal.newUpdateDelay;
emit RoleUpdateAccepted(_role, _roleUpdateProposal.newAddress);
delete roleUpdateProposals[_role];
}

/// @notice rejects a role update
/// @param _role the role to update
function rejectRoleUpdate(bytes32 _role) external {
_roleRestricted(keccak256("MAIN_GUARDIAN"));
RoleUpdate storage _roleUpdateProposal = roleUpdateProposals[_role];
require(_roleUpdateProposal.newAddress != address(0), "RoleManager: Role update not proposed");
delete roleUpdateProposals[_role];
emit RoleUpdateRejected(_role);
}

/* Internal Functions */
/// @notice restricts the function to the role
/// @param _role the role to restrict the function to
function _roleRestricted(bytes32 _role) internal view {
require(msg.sender == roles[_role].roleAddress, "RoleManager: Only role can call this function");
}
}
Loading
Loading