Skip to content

Commit

Permalink
Merge pull request #43 from helix-bridge/xiaoch05-xtokenbridge
Browse files Browse the repository at this point in the history
xtoken bridge v3
  • Loading branch information
xiaoch05 authored Jan 3, 2024
2 parents f210777 + d305154 commit b34abef
Show file tree
Hide file tree
Showing 30 changed files with 7,277 additions and 3 deletions.
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

0 comments on commit b34abef

Please sign in to comment.