Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xtoken bridge v3 #43

Merged
merged 26 commits into from
Jan 3, 2024
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
36 changes: 36 additions & 0 deletions helix-contract/address/xtoken-dev.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"messagers": {
"crab": {
"msglineMessager": "0xf85638B61E0425D6BB91981190B73246e3AF3CA9"
},
"sepolia": {
"msglineMessager": "0xc876D0873e4060472334E297b2db200Ca10cc806"
},
"tron": {
"msglineMessager": "TR3nibHkcXovd1nsuNrLWigQboj4uduhKT"
}
},
"backingProxy": {
"crab": "0xbdC7bbF408931C5d666b4F0520E0D9E9A0B04e99"
},
"backingLogic": {
"crab": "0x01F53415adC20a2D058DfF14e295Ab955CafD6d6"
},
"issuingProxy": {
"sepolia": "0xf22D0bb66b39745Ae6e3fEa3E5859d7f0b367Fd1",
"tron": "TJK57bJTvnaNRGFHwbihg1bXtgnyed6sKa"
},
"issuingLogic": {
"sepolia": "0xCD1c1C799f3914ECFC5e3653D3Cc846355d3dFC9",
"tron": "TD7VoAQnJDKsWsgZZTeeHvBKY5fogRDVc2"
},
"proxyAdmin": {
"tron": "TQuYHJyHkE6wS5uyhWVDAibosNkvMYgyVF"
},
"xToken": {
"tron": "TRXTkfGxTL8CjuGgz55BYkTAyVyiEMFY6F"
},
"guard": {
"sepolia": "0x8F207f0e9Ed3CC1487C5C8981213AD4482d4a972"
}
}
27 changes: 27 additions & 0 deletions helix-contract/contracts/interfaces/IMessageLine.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17;

interface IMessageLine {
function send(uint256 toChainId, address toDapp, bytes calldata message, bytes calldata params) external payable;
function fee(uint256 toChainId, address toDapp, bytes calldata message, bytes calldata params) external view returns (uint256);
}

abstract contract Application {
function _msgSender() internal view returns (address payable _line) {
_line = payable(msg.sender);
}

function _fromChainId() internal pure returns (uint256 _msgDataFromChainId) {
require(msg.data.length >= 52, "!fromChainId");
assembly {
_msgDataFromChainId := calldataload(sub(calldatasize(), 52))
}
}

function _xmsgSender() internal pure returns (address payable _from) {
require(msg.data.length >= 20, "!line");
assembly {
_from := shr(96, calldataload(sub(calldatasize(), 20)))
}
}
}
13 changes: 13 additions & 0 deletions helix-contract/contracts/interfaces/IMessager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17;

interface ILowLevelMessageSender {
function registerRemoteReceiver(uint256 remoteChainId, address remoteBridge) external;
function sendMessage(uint256 remoteChainId, bytes memory message, bytes memory params) external payable;
}

interface ILowLevelMessageReceiver {
function registerRemoteSender(uint256 remoteChainId, address remoteBridge) external;
function recvMessage(address remoteSender, address localReceiver, bytes memory payload) external;
}

1 change: 0 additions & 1 deletion helix-contract/contracts/mapping-token/v2/Guard.sol
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,3 @@ contract Guard is GuardRegistry, Pausable {
return sha256(value);
}
}

297 changes: 297 additions & 0 deletions helix-contract/contracts/mapping-token/v3/GuardRegistryV3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
// SPDX-License-Identifier: Apache-2.0

pragma solidity >=0.8.10;
pragma experimental ABIEncoderV2;

import "@zeppelin-solidity/contracts/utils/cryptography/ECDSA.sol";

/**
* @title Manages a set of guards and a threshold to double-check BEEFY commitment
* @dev Stores the guards and a threshold
* @author echo
*/
contract GuardRegistryV3 {
event AddedGuard(address guard);
event RemovedGuard(address guard);
event ChangedThreshold(uint256 threshold);

// keccak256(
// "EIP712Domain(uint256 chainId,address verifyingContract)"
// );
bytes32 internal constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;

address internal constant SENTINEL_GUARDS = address(0x1);

/**
* @dev Nonce to prevent replay of update operations
*/
uint256 public nonce;
/**
* @dev Store all guards in the linked list
*/
mapping(address => address) internal guards;
/**
* @dev Count of all guards
*/
uint256 internal guardCount;
/**
* @dev Number of required confirmations for update operations
*/
uint256 internal threshold;

/**
* @dev Sets initial storage of contract.
* @param _guards List of Safe guards.
* @param _threshold Number of required confirmations for check commitment or change guards.
*/
function initialize(address[] memory _guards, uint256 _threshold) internal {
// Threshold can only be 0 at initialization.
// Check ensures that setup function can only be called once.
require(threshold == 0, "Guard: Guards have already been setup");
// Validate that threshold is smaller than number of added guards.
require(_threshold <= _guards.length, "Guard: Threshold cannot exceed guard count");
// There has to be at least one Safe guard.
require(_threshold >= 1, "Guard: Threshold needs to be greater than 0");
// Initializing Safe guards.
address currentGuard = SENTINEL_GUARDS;
for (uint256 i = 0; i < _guards.length; i++) {
// Guard address cannot be null.
address guard = _guards[i];
require(guard != address(0) && guard != SENTINEL_GUARDS && guard != address(this) && currentGuard != guard, "Guard: Invalid guard address provided");
// No duplicate guards allowed.
require(guards[guard] == address(0), "Guard: Address is already an guard");
guards[currentGuard] = guard;
currentGuard = guard;
emit AddedGuard(guard);
}
guards[currentGuard] = SENTINEL_GUARDS;
guardCount = _guards.length;
threshold = _threshold;
}

/**
* @dev Allows to add a new guard to the registry and update the threshold at the same time.
* This can only be done via multi-sig.
* @notice Adds the guard `guard` to the registry and updates the threshold to `_threshold`.
* @param guard New guard address.
* @param _threshold New threshold.
* @param signatures The signatures of the guards which to add new guard and update the `threshold` .
*/
function addGuardWithThreshold(
address guard,
uint256 _threshold,
bytes[] memory signatures
) public {
// Guard address cannot be null, the sentinel or the registry itself.
require(guard != address(0) && guard != SENTINEL_GUARDS && guard != address(this), "Guard: Invalid guard address provided");
// No duplicate guards allowed.
require(guards[guard] == address(0), "Guard: Address is already an guard");
verifyGuardSignatures(msg.sig, abi.encode(guard, _threshold), signatures);
guards[guard] = guards[SENTINEL_GUARDS];
guards[SENTINEL_GUARDS] = guard;
guardCount++;
emit AddedGuard(guard);
// Change threshold if threshold was changed.
if (threshold != _threshold) _changeThreshold(_threshold);
}

/**
* @dev Allows to remove an guard from the registry and update the threshold at the same time.
* This can only be done via multi-sig.
* @notice Removes the guard `guard` from the registry and updates the threshold to `_threshold`.
* @param prevGuard Guard that pointed to the guard to be removed in the linked list
* @param guard Guard address to be removed.
* @param _threshold New threshold.
* @param signatures The signatures of the guards which to remove a guard and update the `threshold` .
*/
function removeGuard(
address prevGuard,
address guard,
uint256 _threshold,
bytes[] memory signatures
) public {
// Only allow to remove an guard, if threshold can still be reached.
require(guardCount - 1 >= _threshold, "Guard: Threshold cannot exceed guard count");
// Validate guard address and check that it corresponds to guard index.
require(guard != address(0) && guard != SENTINEL_GUARDS, "Guard: Invalid guard address provided");
require(guards[prevGuard] == guard, "Guard: Invalid prevGuard, guard pair provided");
verifyGuardSignatures(msg.sig, abi.encode(prevGuard, guard, _threshold), signatures);
guards[prevGuard] = guards[guard];
guards[guard] = address(0);
guardCount--;
emit RemovedGuard(guard);
// Change threshold if threshold was changed.
if (threshold != _threshold) _changeThreshold(_threshold);
}

/**
* @dev Allows to swap/replace a guard from the registry with another address.
* This can only be done via multi-sig.
* @notice Replaces the guard `oldGuard` in the registry with `newGuard`.
* @param prevGuard guard that pointed to the guard to be replaced in the linked list
* @param oldGuard guard address to be replaced.
* @param newGuard New guard address.
* @param signatures The signatures of the guards which to swap/replace a guard and update the `threshold` .
*/
function swapGuard(
address prevGuard,
address oldGuard,
address newGuard,
bytes[] memory signatures
) public {
// Guard address cannot be null, the sentinel or the registry itself.
require(newGuard != address(0) && newGuard != SENTINEL_GUARDS && newGuard != address(this), "Guard: Invalid guard address provided");
// No duplicate guards allowed.
require(guards[newGuard] == address(0), "Guard: Address is already an guard");
// Validate oldGuard address and check that it corresponds to guard index.
require(oldGuard != address(0) && oldGuard != SENTINEL_GUARDS, "Guard: Invalid guard address provided");
require(guards[prevGuard] == oldGuard, "Guard: Invalid prevGuard, guard pair provided");
verifyGuardSignatures(msg.sig, abi.encode(prevGuard, oldGuard, newGuard), signatures);
guards[newGuard] = guards[oldGuard];
guards[prevGuard] = newGuard;
guards[oldGuard] = address(0);
emit RemovedGuard(oldGuard);
emit AddedGuard(newGuard);
}

/**
* @dev Allows to update the number of required confirmations by guards.
* This can only be done via multi-sig.
* @notice Changes the threshold of the registry to `_threshold`.
* @param _threshold New threshold.
* @param signatures The signatures of the guards which to update the `threshold` .
*/
function changeThreshold(uint256 _threshold, bytes[] memory signatures) public {
verifyGuardSignatures(msg.sig, abi.encode(_threshold), signatures);
_changeThreshold(_threshold);
}

function _changeThreshold(uint256 _threshold) internal {
// Validate that threshold is smaller than number of owners.
require(_threshold <= guardCount, "Guard: Threshold cannot exceed guard count");
// There has to be at least one guard.
require(_threshold >= 1, "Guard: Threshold needs to be greater than 0");
threshold = _threshold;
emit ChangedThreshold(threshold);
}

function getThreshold() public view returns (uint256) {
return threshold;
}

function isGuard(address guard) public view returns (bool) {
return guard != SENTINEL_GUARDS && guards[guard] != address(0);
}

/**
* @dev Returns array of guards.
* @return Array of guards.
*/
function getGuards() public view returns (address[] memory) {
address[] memory array = new address[](guardCount);

// populate return array
uint256 index = 0;
address currentGuard = guards[SENTINEL_GUARDS];
while (currentGuard != SENTINEL_GUARDS) {
array[index] = currentGuard;
currentGuard = guards[currentGuard];
index++;
}
return array;
}

function verifyGuardSignatures(
bytes4 methodID,
bytes memory params,
bytes[] memory signatures
) internal {
bytes32 structHash =
keccak256(
abi.encode(
methodID,
params,
nonce
)
);
checkGuardSignatures(structHash, signatures);
nonce++;
}

function verifyGuardSignaturesWithoutNonce(
bytes4 methodID,
bytes memory params,
bytes[] memory signatures
) view internal {
bytes32 structHash =
keccak256(
abi.encode(
methodID,
params
)
);
checkGuardSignatures(structHash, signatures);
}

/**
* @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
* @param structHash The struct Hash of the data (could be either a message/commitment hash).
* @param signatures Signature data that should be verified. only ECDSA signature.
* Signers need to be sorted in ascending order
*/
function checkGuardSignatures(
bytes32 structHash,
bytes[] memory signatures
) public view {
// Load threshold to avoid multiple storage loads
uint256 _threshold = threshold;
// Check that a threshold is set
require(_threshold > 0, "Guard: Threshold needs to be defined");
bytes32 dataHash = encodeDataHash(structHash);
checkNSignatures(dataHash, signatures, _threshold);
}

/**
* @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
* @param dataHash Hash of the data (could be either a message hash or transaction hash).
* @param signatures Signature data that should be verified. only ECDSA signature.
* Signers need to be sorted in ascending order
* @param requiredSignatures Amount of required valid signatures.
*/
function checkNSignatures(
bytes32 dataHash,
bytes[] memory signatures,
uint256 requiredSignatures
) public view {
// Check that the provided signature data is not too short
require(signatures.length >= requiredSignatures, "GS020");
// There cannot be an owner with address 0.
address lastGuard = address(0);
address currentGuard;
for (uint256 i = 0; i < requiredSignatures; i++) {
currentGuard = ECDSA.recover(dataHash, signatures[i]);
require(currentGuard > lastGuard && guards[currentGuard] != address(0) && currentGuard != SENTINEL_GUARDS, "Guard: Invalid guard provided");
lastGuard = currentGuard;
}
}

/**
* @dev Returns the chain id used by this contract.
*/
function getChainId() public view returns (uint256) {
uint256 id;
// solhint-disable-next-line no-inline-assembly
assembly {
id := chainid()
}
return id;
}

function domainSeparator() public view returns (bytes32) {
return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), address(this)));
}

function encodeDataHash(bytes32 structHash) public view returns (bytes32) {
return keccak256(abi.encodePacked(hex"1901", domainSeparator(), structHash));
}
}
Loading
Loading