Skip to content

Commit

Permalink
Merge the develop branch to the master branch, preparation to v5.7.0-rc0
Browse files Browse the repository at this point in the history
  • Loading branch information
akolotov authored Apr 13, 2021
2 parents cdef2c6 + 37e92f3 commit c937711
Show file tree
Hide file tree
Showing 16 changed files with 392 additions and 30 deletions.
65 changes: 65 additions & 0 deletions contracts/helpers/AMBBridgeHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
pragma solidity 0.4.24;

interface IHomeBridge {
function numMessagesSigned(bytes32 _message) external view returns (uint256);
function isAlreadyProcessed(uint256 _number) external pure returns (bool);
function signature(bytes32 _hash, uint256 _index) external view returns (bytes memory);
}

contract Helper {
function unpackSignature(bytes memory _signature) internal pure returns (bytes32 r, bytes32 s, uint8 v) {
require(_signature.length == 65);

assembly {
r := mload(add(_signature, 0x20))
s := mload(add(_signature, 0x40))
v := mload(add(_signature, 0x41))
}
return (r, s, v);
}
}

contract AMBBridgeHelper is Helper {
address public owner;
IHomeBridge public ambBridge;

constructor(address _homeBridge) public {
owner = msg.sender;
ambBridge = IHomeBridge(_homeBridge);
}

function getSignatures(bytes _message) external view returns (bytes memory) {
bytes32 msgHash = keccak256(abi.encodePacked(_message));
uint256 signed = ambBridge.numMessagesSigned(msgHash);

require(ambBridge.isAlreadyProcessed(signed), "message hasn't been confirmed");

// recover number of confirmations sent by oracles
signed = signed & 0x8fffffffffffffffffffffffffffffffffffffffffff;

require(signed < 0x100);

bytes memory signatures = new bytes(1 + signed * 65);

assembly {
mstore8(add(signatures, 32), signed)
}

for (uint256 i = 0; i < signed; i++) {
bytes memory sig = ambBridge.signature(msgHash, i);
(bytes32 r, bytes32 s, uint8 v) = unpackSignature(sig);
assembly {
mstore8(add(add(signatures, 33), i), v)
mstore(add(add(add(signatures, 33), signed), mul(i, 32)), r)
mstore(add(add(add(signatures, 33), mul(signed, 33)), mul(i, 32)), s)
}
}

return signatures;
}

function clean() external {
require(msg.sender == owner, "not an owner");
selfdestruct(owner);
}
}
79 changes: 79 additions & 0 deletions contracts/helpers/Erc20ToNativeBridgeHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
pragma solidity 0.4.24;

interface IHomeErc20ToNativeBridge {
function numMessagesSigned(bytes32 _message) external view returns (uint256);
function isAlreadyProcessed(uint256 _number) external pure returns (bool);
function message(bytes32 _hash) external view returns (bytes memory);
function signature(bytes32 _hash, uint256 _index) external view returns (bytes memory);
}

contract Helper {
function unpackSignature(bytes memory _signature) internal pure returns (bytes32 r, bytes32 s, uint8 v) {
require(_signature.length == 65);

assembly {
r := mload(add(_signature, 0x20))
s := mload(add(_signature, 0x40))
v := mload(add(_signature, 0x41))
}
return (r, s, v);
}
}

contract Erc20ToNativeBridgeHelper is Helper {
address public owner;
IHomeErc20ToNativeBridge public bridge;
address public foreignBridge;

constructor(address _homeBridge, address _foreignBridge) public {
owner = msg.sender;
bridge = IHomeErc20ToNativeBridge(_homeBridge);
foreignBridge = _foreignBridge;
}

function getMessage(bytes32 _msgHash) external view returns (bytes memory result) {
result = bridge.message(_msgHash);
}

function getMessageHash(address _recipient, uint256 _value, bytes32 _origTxHash) external view returns (bytes32) {
bytes32 result = keccak256(abi.encodePacked(_recipient, _value, _origTxHash, foreignBridge));
return result;
}

function getSignatures(bytes32 _msgHash) external view returns (bytes memory) {
uint256 signed = bridge.numMessagesSigned(_msgHash);

require(bridge.isAlreadyProcessed(signed), "message hasn't been confirmed");

// recover number of confirmations sent by oracles
signed = signed & 0x8fffffffffffffffffffffffffffffffffffffffffff;

require(signed < 0x100);

bytes memory signatures = new bytes(1 + signed * 65);

assembly {
mstore8(add(signatures, 32), signed)
}

for (uint256 i = 0; i < signed; i++) {
bytes memory sig = bridge.signature(_msgHash, i);
(bytes32 r, bytes32 s, uint8 v) = unpackSignature(sig);
assembly {
let ptr := add(signatures, 33)
mstore8(add(ptr, i), v)
ptr := add(ptr, signed)
mstore(add(ptr, mul(i, 32)), r)
ptr := add(ptr, mul(signed, 32))
mstore(add(ptr, mul(i, 32)), s)
}
}

return signatures;
}

function clean() external {
require(msg.sender == owner, "not an owner");
selfdestruct(owner);
}
}
103 changes: 95 additions & 8 deletions contracts/upgradeable_contracts/arbitrary_message/BasicForeignAMB.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,111 @@ contract BasicForeignAMB is BasicAMB, MessageRelay, MessageDelivery {
* @param _data bytes to be relayed
* @param _signatures bytes blob with signatures to be validated
*/
function executeSignatures(bytes _data, bytes _signatures) external {
Message.hasEnoughValidSignatures(_data, _signatures, validatorContract(), true);
function executeSignatures(bytes _data, bytes _signatures) public {
_allowMessageExecution(_data, _signatures);

bytes32 messageId;
bytes32 msgId;
address sender;
address executor;
uint32 gasLimit;
uint8 dataType;
uint256[2] memory chainIds;
bytes memory data;

(messageId, sender, executor, gasLimit, dataType, chainIds, data) = ArbitraryMessage.unpackData(_data);
(msgId, sender, executor, gasLimit, dataType, chainIds, data) = ArbitraryMessage.unpackData(_data);

_executeMessage(msgId, sender, executor, gasLimit, dataType, chainIds, data);
}

/**
* @dev Validates provided signatures and relays a given message.
* The message is not allowed to fail. The whole tx will be revered if message fails.
* @param _data bytes to be relayed
* @param _signatures bytes blob with signatures to be validated
*/
function safeExecuteSignatures(bytes _data, bytes _signatures) external {
executeSignatures(_data, _signatures);
}

/**
* @dev Validates provided signatures and relays a given message. Allows to override the gas limit of the passed message.
* Usually it makes sense to provide a higher amount of gas for the execution.
* The message is not allowed to fail. The whole tx will be revered if message fails.
* @param _data bytes to be relayed
* @param _signatures bytes blob with signatures to be validated
*/
function safeExecuteSignaturesWithGasLimit(bytes _data, bytes _signatures, uint32 _gas) public {
_allowMessageExecution(_data, _signatures);

bytes32 msgId;
address sender;
address executor;
uint8 dataType;
uint256[2] memory chainIds;
bytes memory data;

(msgId, sender, executor, , dataType, chainIds, data) = ArbitraryMessage.unpackData(_data);

require(_isMessageVersionValid(messageId));
_executeMessage(msgId, sender, executor, _gas, dataType, chainIds, data);
}

/**
* @dev Validates provided signatures and relays a given message. Passes all available gas for the execution.
* The message is not allowed to fail. The whole tx will be revered if message fails.
* @param _data bytes to be relayed
* @param _signatures bytes blob with signatures to be validated
*/
function safeExecuteSignaturesWithAutoGasLimit(bytes _data, bytes _signatures) external {
safeExecuteSignaturesWithGasLimit(_data, _signatures, 0xffffffff);
}

/**
* @dev Internal function for validating pre-execution requirements.
* @param _data bytes to be relayed.
* @param _signatures bytes blob with signatures to be validated.
*/
function _allowMessageExecution(bytes _data, bytes _signatures) internal {
// this checks prevents execution of other messages, while some other message is being processed
// nested executeSignatures is considered to be unsafe,
// since it allows to change/reset the AMB context variables (messageId, messageSender, messageSourceChainId)
// while processing nested message
require(messageId() == bytes32(0));

Message.hasEnoughValidSignatures(_data, _signatures, validatorContract(), true);
}

/**
* @dev Internal function for executing decoded message. Performs additional validation on the message fields.
* @param msgId id of the processed message.
* @param sender sender address on the other side.
* @param executor address of an executor.
* @param gasLimit gas limit for a call to executor.
* @param dataType AMB message dataType to be included as a part of the header.
* @param chainIds pair of source and destination chain ids.
* @param data calldata for a call to executor.
*/
function _executeMessage(
bytes32 msgId,
address sender,
address executor,
uint32 gasLimit,
uint8 dataType,
uint256[2] memory chainIds,
bytes memory data
) internal {
require(_isMessageVersionValid(msgId));
require(_isDestinationChainIdValid(chainIds[1]));
require(!relayedMessages(messageId));
setRelayedMessages(messageId, true);
processMessage(sender, executor, messageId, gasLimit, dataType, chainIds[0], data);
require(!relayedMessages(msgId));
setRelayedMessages(msgId, true);
processMessage(sender, executor, msgId, gasLimit, dataType, chainIds[0], data);
}

/**
* @dev Validates message execution status. Reverts if message is was executed in safe mode and reverted.
* @param _status message execution status.
*/
function _validateExecutionStatus(bool _status) internal {
require(_status || msg.sig == this.executeSignatures.selector);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ contract MessageProcessor is EternalStorage {
* @param _sender sender address on the other side.
* @param _contract address of an executor contract.
* @param _data calldata for a call to executor.
* @param _gas gas limit for a call to executor.
* @param _gas gas limit for a call to executor. 2^32 - 1, if caller will pass all available gas for the execution.
* @param _messageId id of the processed message.
* @param _sourceChainId source chain id is of the received message.
*/
Expand All @@ -230,15 +230,24 @@ contract MessageProcessor is EternalStorage {
// only because the oracle provides incorrect gas limit for the transaction
// This check is needed here in order to force contract to pass exactly the requested amount of gas.
// Avoiding it may lead to the unwanted message failure in some extreme cases.
require((gasleft() * 63) / 64 > _gas);
require(_gas == 0xffffffff || (gasleft() * 63) / 64 > _gas);

bool status = _contract.call.gas(_gas)(_data);
_validateExecutionStatus(status);
setMessageSender(address(0));
setMessageId(bytes32(0));
setMessageSourceChainId(0);
return status;
}

/**
* @dev Validates message execution status. In simplest case, does nothing.
* @param _status message execution status.
*/
function _validateExecutionStatus(bool _status) internal {
(_status);
}

/* solcov ignore next */
function emitEventOnMessageProcessed(address sender, address executor, bytes32 messageId, bool status) internal;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ contract VersionableAMB is VersionableBridge {
* @return (major, minor, patch) version triple
*/
function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
return (5, 6, 0);
return (5, 7, 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ contract BasicMultiAMBErc20ToErc677 is
* @return patch value of the version
*/
function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
return (1, 4, 0);
return (1, 5, 0);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ contract BasicMultiTokenBridge is EternalStorage, Ownable {
* @return minimum amount on tokens that can be sent through the bridge in one transfer.
*/
function minPerTx(address _token) public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("minPerTx", _token))];
uint256 limit = uintStorage[keccak256(abi.encodePacked("minPerTx", _token))];
if (_token == address(0)) {
return limit;
}
return limit > 0 ? 1 : 0;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,13 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 {
// which will call passMessage.
require(!lock());

uint256 balanceBefore = token.balanceOf(address(this));
setLock(true);
token.safeTransferFrom(msg.sender, _value);
setLock(false);
bridgeSpecificActionsOnTokenTransfer(token, msg.sender, _receiver, _value);
uint256 balanceDiff = token.balanceOf(address(this)).sub(balanceBefore);
require(balanceDiff <= _value);
bridgeSpecificActionsOnTokenTransfer(token, msg.sender, _receiver, balanceDiff);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ contract HomeFeeManagerMultiAMBErc20ToErc677 is BaseRewardAddressList, Ownable,
* @param _fee new fee value, in percentage (1 ether == 10**18 == 100%).
*/
function setFee(bytes32 _feeType, address _token, uint256 _fee) external onlyOwner {
require(isTokenRegistered(_token));
_setFee(_feeType, _token, _fee);
}

Expand All @@ -76,6 +77,19 @@ contract HomeFeeManagerMultiAMBErc20ToErc677 is BaseRewardAddressList, Ownable,
* @return fee value associated with the requested fee type.
*/
function getFee(bytes32 _feeType, address _token) public view validFeeType(_feeType) returns (uint256) {
if (_getFee(_feeType, _token) > 0) {
return _getFee(_feeType, address(0));
}
return 0;
}

/**
* @dev Internal function for reading fee values mapping.
* @param _feeType type of the fee, can be one of [HOME_TO_FOREIGN_FEE, FOREIGN_TO_HOME_FEE].
* @param _token address of the token contract for which fee should apply.
* @return fee value associated with the requested fee type.
*/
function _getFee(bytes32 _feeType, address _token) internal returns (uint256) {
return uintStorage[keccak256(abi.encodePacked(_feeType, _token))];
}

Expand All @@ -98,7 +112,6 @@ contract HomeFeeManagerMultiAMBErc20ToErc677 is BaseRewardAddressList, Ownable,
* @param _fee new fee value, in percentage (1 ether == 10**18 == 100%).
*/
function _setFee(bytes32 _feeType, address _token, uint256 _fee) internal validFeeType(_feeType) validFee(_fee) {
require(isTokenRegistered(_token));
uintStorage[keccak256(abi.encodePacked(_feeType, _token))] = _fee;
emit FeeUpdated(_feeType, _token, _fee);
}
Expand Down
Loading

0 comments on commit c937711

Please sign in to comment.