Skip to content

Commit

Permalink
Callback for app layer to aware the message dispatch result (#99)
Browse files Browse the repository at this point in the history
* on message delivery

* fix

* rename

* increase timeout

* add some doc
  • Loading branch information
hujw77 committed Nov 30, 2021
1 parent 5365532 commit 0bdf8eb
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 4 deletions.
29 changes: 27 additions & 2 deletions contracts/bridge/contracts/common/message/OutboundLane.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "../../interfaces/IOutboundLane.sol";
import "../../interfaces/IOnMessageDelivered.sol";
import "./MessageVerifier.sol";
import "./TargetChain.sol";
import "./SourceChain.sol";
Expand All @@ -29,8 +30,10 @@ contract OutboundLane is IOutboundLane, MessageVerifier, TargetChain, SourceChai
event MessagePruned(uint64 oldest_unpruned_nonce);
event MessageFeeIncreased(uint64 nonce, uint256 fee);
event RelayerReward(address relayer, uint256 reward);
event CallbackMessageDelivered(uint64 nonce, bool result);

bytes32 internal constant OUTBOUND_ROLE = keccak256("OUTBOUND_ROLE");
uint256 internal constant MAX_GAS_PER_MESSAGE = 100000;
uint64 internal constant MAX_PENDING_MESSAGES = 50;
uint64 internal constant MAX_PRUNE_MESSAGES_ATONCE = 10;

Expand Down Expand Up @@ -79,7 +82,13 @@ contract OutboundLane is IOutboundLane, MessageVerifier, TargetChain, SourceChai
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
}

// Send message over lane.
/**
* @notice Send message over lane.
* Submitter could be a contract or just an EOA address.
* At the beginning of the launch, submmiter is permission, after the system is stable it will be permissionless.
* @param targetContract The target contract address which you would send cross chain message to
* @param encoded The calldata which encoded by ABI Encoding
*/
function send_message(address targetContract, bytes calldata encoded) external payable override nonReentrant returns (uint64) {
require(hasRole(OUTBOUND_ROLE, msg.sender), "Lane: NotAuthorized");
require(outboundLaneNonce.latest_generated_nonce - outboundLaneNonce.latest_received_nonce <= MAX_PENDING_MESSAGES, "Lane: TooManyPendingMessages");
Expand Down Expand Up @@ -135,7 +144,7 @@ contract OutboundLane is IOutboundLane, MessageVerifier, TargetChain, SourceChai
) public nonReentrant {
verify_lane_data_proof(hash(inboundLaneData), messagesProof);
DeliveredMessages memory confirmed_messages = confirm_delivery(inboundLaneData);
// TODO: callback `on_messages_delivered`
on_messages_delivered(confirmed_messages);
pay_relayers_rewards(inboundLaneData.relayers, confirmed_messages.begin, confirmed_messages.end);
commit();
}
Expand Down Expand Up @@ -245,6 +254,22 @@ contract OutboundLane is IOutboundLane, MessageVerifier, TargetChain, SourceChai
}
}

function on_messages_delivered(DeliveredMessages memory confirmed_messages) internal {
for (uint64 nonce = confirmed_messages.begin; nonce <= confirmed_messages.end; nonce ++) {
uint256 offset = nonce - confirmed_messages.begin;
bool dispatch_result = ((confirmed_messages.dispatch_results >> offset) & 1) > 0;
// Submitter could be a contract or just an EOA address.
address submitter = messages[nonce].payload.sourceAccount;
bytes memory deliveredCallbackData = abi.encodeWithSelector(
IOnMessageDelivered.on_messages_delivered.selector,
nonce,
dispatch_result
);
(bool ok,) = submitter.call{value: 0, gas: MAX_GAS_PER_MESSAGE}(deliveredCallbackData);
emit CallbackMessageDelivered(nonce, ok);
}
}

/// Prune at most `max_messages_to_prune` already received messages.
///
/// Returns number of pruned messages.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.7.0;
pragma solidity >=0.6.0;

/**
* @title A interface for message layer to filter unsafe message
Expand Down
17 changes: 17 additions & 0 deletions contracts/bridge/contracts/interfaces/IOnMessageDelivered.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0;

/**
* @title A interface for app layer to get message dispatch result
* @author echo
* @notice The app layer could implement the interface `IOnMessageDelivered` to receive message dispatch result (optionally)
*/
interface IOnMessageDelivered {
/**
* @notice Message delivered callback
* @param nonce Nonce of the callback message
* @param dispatch_result Dispatch result of cross chain message
*/
function on_messages_delivered(uint64 nonce, bool dispatch_result) external;
}
2 changes: 1 addition & 1 deletion contracts/bridge/test/v1/2_test_light_client_gas.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ describe("Light Client Gas Usage", function () {
for (const testCase of testCases) {
it(`runs full flow with ${testCase.totalNumberOfValidators} validators and ${testCase.totalNumberOfSignatures} signers with the complete transaction succeeding`,
async function () {
this.timeout(1000 * 100);
this.timeout(1000 * 200);
await runFlow(testCase.totalNumberOfValidators, testCase.totalNumberOfSignatures)
});
}
Expand Down
5 changes: 5 additions & 0 deletions contracts/bridge/test/v2/test_sync_message_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ const receive_messages_delivery_proof = async (begin, end) => {
await expect(tx)
.to.emit(outbound, "MessagesDelivered")
.withArgs(begin, end, 0)
for (let i = begin; i<=end; i++) {
await expect(tx)
.to.emit(outbound, "CallbackMessageDelivered")
.withArgs(i, true)
}
await logNonce()
}

Expand Down

0 comments on commit 0bdf8eb

Please sign in to comment.