-
Notifications
You must be signed in to change notification settings - Fork 228
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
Multi AMB requests in one transaction #403
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the improvement!
I have few remarks:
- Please add description headers to the methods you are changing.
- Since we have adopters already that developed and deployed their extensions for AMB (ETH-POA and ETH-xDAI) it seems that we need to start supporting two options to call
requireToPassMessage
from the mediator contracts. - We need to think of the procedure to upgrade existing bridges - how the contracts could be upgraded as so the previous requests to pass messages cannot be replayed.
Regarding 2.How we should distinguish these two functions? We cannot use both:
, they have the same signature. The old version of mediators can successfully call new version of
If we do so, then the old version of mediator still will be able to do something like: function requestFailedMessageFix(bytes32 _txHash) external {
require(!bridgeContract().messageCallStatus(_txHash));
require(bridgeContract().failedMessageReceiver(_txHash) == address(this));
require(bridgeContract().failedMessageSender(_txHash) == mediatorContractOnOtherSide());
bytes32 dataHash = bridgeContract().failedMessageDataHash(_txHash);
bytes4 methodSelector = this.fixFailedMessage.selector;
bytes memory data = abi.encodeWithSelector(methodSelector, dataHash);
bridgeContract().requireToPassMessage(mediatorContractOnOtherSide(), data, requestGasLimit());
} And the new version mediator will be able to do something like: function requestFailedMessageFix(bytes32 messageId) external {
require(!bridgeContract().messageCallStatus(messageId));
require(bridgeContract().failedMessageReceiver(messageId) == address(this));
require(bridgeContract().failedMessageSender(messageId) == mediatorContractOnOtherSide());
bytes4 methodSelector = this.fixFailedMessage.selector;
bytes memory data = abi.encodeWithSelector(methodSelector, messageId);
bridgeContract().requireToPassMessage(mediatorContractOnOtherSide(), data, requestGasLimit());
} Regarding 3.Replay attack won't be possible, since the message structure and processing logic didn't change. |
I agree on the item 2. Thanks. But for the item 3 we need to think more. Since the tx hash now is not the part of the message could it compromise security? In other words, if the message id is unique enough? E.g. it could be the same in different chains if the bridge contract was deployed from the same account with the same number of transaction (nonce). Is a similar property applicable for the tx hashes and they also could be non-unique? |
This PR will eventually close #373 also. The existing bridge does not have any failed messages, so the support of previously used approach is not needed. |
Let's prepare the changes for the oracle as well. We can trail the upgrade then on the bridge between Kovan and Sokol. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per offline discussion please consider to extend the message sending through the bridge as following:
version[4] -----|
address[20] | message id
nonce[8] -----|
chainId[32]
sender[20]
executor[20]
gas[4]
dataType[1]
data[…]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another set of the comments
*/ | ||
function distributeFee(IMediatorFeeManager _feeManager, uint256 _fee, bytes32 _txHash) internal { | ||
function distributeFee(IMediatorFeeManager _feeManager, uint256 _fee, bytes32 _messageId) internal { | ||
// Right now, AMB bridge supports only one message per transaction. | ||
// The receivers of the fee could try to send back the fees through the mediator, | ||
// so here we add a lock to limit the number of messages that the mediator can send to the bridge, | ||
// allowing a maximum of 1 message | ||
enableMessagesRestriction(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, let's leave this as is now and address in a separate issue. We need to commit the changes for this new functionality in short time.
contracts/upgradeable_contracts/arbitrary_message/BasicForeignAMB.sol
Outdated
Show resolved
Hide resolved
contracts/upgradeable_contracts/arbitrary_message/BasicHomeAMB.sol
Outdated
Show resolved
Hide resolved
contracts/upgradeable_contracts/arbitrary_message/BasicForeignAMB.sol
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider suggestions to use integers for the chain ids rather than set of bytes.
contracts/upgradeable_contracts/arbitrary_message/MessageDelivery.sol
Outdated
Show resolved
Hide resolved
header = new bytes(79 + sourceChainIdLength + destinationChainIdLength); | ||
|
||
assembly { | ||
let ptr := add(header, mload(header)) // points to the last word of header |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I think you need to add a comment before the assembly part saying that "in order to safe gas the header is packed from the end"
bytes32 mVer = MESSAGE_PACKING_VERSION; | ||
uint256 nonce = _nonce(); | ||
|
||
bytes32 bridgeId = keccak256(abi.encodePacked(sourceChainId, address(this))) & |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also add a comment here "it does not make sense to keep the bridge Id in a storage since the operation to read it will take 800 gas whereas the operations to get sha3 plus to combine the integer and the address into the byte blob will consume less"
mstore(ptr, destinationChainId) | ||
mstore(sub(ptr, destinationChainIdLength), sourceChainId) | ||
|
||
// can be ommitted for dataType 0x00, since header is already initialized with zeros |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// can be ommitted for dataType 0x00, since header is already initialized with zeros | |
// can be ommitted for dataType 0x00, since header is already initialized with zeros after the previous mstore operation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case chainId
is 32 bytes, I think that this zero will come from header = new bytes(79 + sourceChainIdLength + destinationChainIdLength);
line. This bytes blob should be allocated from the free memory pointer at 0x40
, so it will be pre-filled with zeros.
Am I right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure that it is pre-filled with zeros. Did you look at the decompiled solidity code to confirm this? Because, in general, the solidity considerations will work even for the next code to allocate a new set of bytes in the memory:
assembly {
new_ptr := mload(0x40)
mstore(0x40, add(ptr, and(add(add(sizeOfNewDataBlob, 0x20), 0x1f), not(0x1f))))
}
As you can see it does not assume that the free memory is zeroed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, then I think it makes sense to uncomment this mstore
operation, it should not consume a lot of gas.
Resolves #307