Skip to content
This repository has been archived by the owner on Mar 7, 2023. It is now read-only.

Commit

Permalink
Remove constraint that caller must be a contract
Browse files Browse the repository at this point in the history
  • Loading branch information
sink772 committed Jan 27, 2023
1 parent b1d72b9 commit 959681d
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public interface CallService {
/*======== At the source CALL_BSH ========*/
/**
* Sends a call message to the contract on the destination chain.
* Only allowed to be called from the contract.
*
* @param _to The BTP address of the callee on the destination chain
* @param _data The calldata specific to the target contract
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ private void cleanupCallRequest(BigInteger sn) {
@External
public BigInteger sendCallMessage(String _to, byte[] _data, @Optional byte[] _rollback) {
Address caller = Context.getCaller();
Context.require(caller.isContract(), "SenderNotAContract");
// check if caller is a contract or rollback data is null in case of EOA
Context.require(caller.isContract() || _rollback == null, "RollbackNotPossible");

// check size of payloads to avoid abusing
Context.require(_data.length <= MAX_DATA_SIZE, "MaxDataSizeExceeded");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ void handleBTPMessageShouldEmitCallMessage() {
var csMsg = new CSMessage(CSMessage.REQUEST, request.toBytes());
var checker = CSIntegrationTest.callMessageEvent((el) -> {
assertEquals(from.toString(), el.getFrom());
assertEquals(sampleAddress.toString(), el.getTo());
assertEquals(to.account(), el.getTo());
assertEquals(srcSn, el.getSn());
assertEquals(reqId, el.getReqId());
assertArrayEquals(request.getData(), el.getData());
Expand Down Expand Up @@ -206,6 +206,55 @@ void maxPayloadsTest() {
}
}

@Order(6)
@Test
void sendCallMessageFromEOA() {
byte[] data = "sendCallMessageFromEOA".getBytes();
var sn = getNextSn();
requestMap.put(sn, new MessageRequest(data, null));
Address caller = Address.of(callSvc._wallet());
var request = new CSMessageRequest(caller.toString(), to.account(), sn, false, data);
var checker = MockBMCIntegrationTest.sendMessageEvent((el) -> {
assertEquals(linkNet, el.getTo());
assertEquals(CallService.NAME, el.getSvc());
assertEquals(BigInteger.ZERO, el.getSn()); // one-way message
CSMessage csMessage = CSMessage.fromBytes(el.getMsg());
assertEquals(CSMessage.REQUEST, csMessage.getType());
AssertCallService.assertEqualsCSMessageRequest(request, CSMessageRequest.fromBytes(csMessage.getData()));
});
BigInteger fee = getFee(to.net(), false);
assertEquals(forwardFee.add(protocolFee), fee);
accumulateFee(fee, protocolFee);

// fail if rollback is provided
AssertRevertedException.assertUserReverted(0, () ->
callSvc.sendCallMessage(fee, to.toString(), data, "fakeRollback".getBytes())
);
// success if rollback is null
callSvc.sendCallMessage(checker, fee, to.toString(), data, null);
}

@Order(7)
@Test
void handleBTPMessageShouldEmitCallMessageFromEOA() {
Address caller = Address.of(callSvc._wallet());
var from = new BTPAddress(linkNet, caller.toString());
var reqId = getNextReqId();
byte[] data = requestMap.get(srcSn).getData();
var request = new CSMessageRequest(from.account(), to.account(), srcSn, false, data);
var csMsg = new CSMessage(CSMessage.REQUEST, request.toBytes());
var checker = CSIntegrationTest.callMessageEvent((el) -> {
assertEquals(from.toString(), el.getFrom());
assertEquals(to.account(), el.getTo());
assertEquals(srcSn, el.getSn());
assertEquals(reqId, el.getReqId());
assertArrayEquals(request.getData(), el.getData());
});
MockBMCIntegrationTest.mockBMC.handleBTPMessage(
checker, csAddress,
linkNet, CallService.NAME, srcSn, csMsg.toBytes());
}

@Order(10)
@Test
void sendCallMessageWithRollback() {
Expand Down
4 changes: 2 additions & 2 deletions solidity/xcall/contracts/CallService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ contract CallService is IBSH, ICallService, IFeeManage, Initializable {
) external payable override returns (
uint256
) {
// Note if caller is a contract in construction, will revert
require(msg.sender.code.length > 0, "SenderNotAContract");
// check if caller is a contract or rollback data is null in case of EOA
require(msg.sender.code.length > 0 || _rollback.length == 0, "RollbackNotPossible");

// check size of payloads to avoid abusing
require(_data.length <= MAX_DATA_SIZE, "MaxDataSizeExceeded");
Expand Down
1 change: 0 additions & 1 deletion solidity/xcall/contracts/interfaces/ICallService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ interface ICallService {
/*======== At the source CALL_BSH ========*/
/**
@notice Sends a call message to the contract on the destination chain.
Only allowed to be called from the contract.
@param _to The BTP address of the callee on the destination chain
@param _data The calldata specific to the target contract
@param _rollback (Optional) The data for restoring the caller state when an error occurred
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ void sendCallMessageWithoutRollback() throws Exception {
@Order(1)
@Test
void handleBTPMessageShouldEmitCallMessage() throws Exception {
var from = new BTPAddress(linkNet, sampleAddress.toString());
var from = new BTPAddress(linkNet, sampleAddress);
var reqId = getNextReqId();
byte[] data = requestMap.get(srcSn).getData();
var request = new CSMessageRequest(from.account(), to.account(), srcSn, false, data);
Expand Down Expand Up @@ -203,6 +203,55 @@ void maxPayloadsTest() throws Exception {
}
}

@Order(6)
@Test
void sendCallMessageFromEOA() throws Exception {
byte[] data = "sendCallMessageFromEOA".getBytes();
var sn = getNextSn();
requestMap.put(sn, new MessageRequest(data, null));
var caller = EVMIntegrationTest.credentials.getAddress();
var request = new CSMessageRequest(caller, to.account(), sn, false, data);
var checker = MockBMCIntegrationTest.sendMessageEvent((el) -> {
assertEquals(linkNet, el._to);
assertEquals(NAME, el._svc);
assertEquals(BigInteger.ZERO, el._sn);
CSMessage csMessage = CSMessage.fromBytes(el._msg);
assertEquals(CSMessage.REQUEST, csMessage.getType());
AssertCallService.assertEqualsCSMessageRequest(request, CSMessageRequest.fromBytes(csMessage.getData()));
});
BigInteger fee = getFee(to.net(), false);
assertEquals(forwardFee.add(protocolFee), fee);
accumulateFee(fee, protocolFee);

// fail if rollback is provided
AssertTransactionException.assertRevertReason(null, () ->
callService.sendCallMessage(to.toString(), data, "fakeRollback".getBytes(), fee).send()
);
// success if rollback is null
checker.accept(callService.sendCallMessage(to.toString(), data, new byte[]{}, fee).send());
}

@Order(7)
@Test
void handleBTPMessageShouldEmitCallMessageFromEOA() throws Exception {
var caller = EVMIntegrationTest.credentials.getAddress();
var from = new BTPAddress(linkNet, caller);
var reqId = getNextReqId();
byte[] data = requestMap.get(srcSn).getData();
var request = new CSMessageRequest(from.account(), to.account(), srcSn, false, data);
var csMsg = new CSMessage(CSMessage.REQUEST, request.toBytes());
var checker = CSIntegrationTest.callMessageEvent((el) -> {
assertEquals(Hash.sha3String(from.toString()), StringUtil.bytesToHex(el._from));
assertEquals(Hash.sha3String(to.account()), StringUtil.bytesToHex(el._to));
assertEquals(srcSn, el._sn);
assertEquals(reqId, el._reqId);
assertArrayEquals(request.getData(), el._data);
});
checker.accept(MockBMCIntegrationTest.mockBMC.handleBTPMessage(
csAddress,
linkNet, NAME, srcSn, csMsg.toBytes()).send());
}

@Order(10)
@Test
void sendCallMessageWithRollback() throws Exception {
Expand All @@ -214,7 +263,7 @@ private void _sendCallMessageWithRollback(BigInteger inc) throws Exception {
byte[] rollback = "ThisIsRollbackMessage".getBytes();
var sn = getNextSn(inc);
requestMap.put(sn, new MessageRequest(data, rollback));
var request = new CSMessageRequest(sampleAddress.toString(), fakeTo.account(), sn, true, data);
var request = new CSMessageRequest(sampleAddress, fakeTo.account(), sn, true, data);
var checker = MockBMCIntegrationTest.sendMessageEvent(
(el) -> {
assertEquals(linkNet, el._to);
Expand All @@ -235,9 +284,8 @@ private void _sendCallMessageWithRollback(BigInteger inc) throws Exception {
@Test
void executeCallWithFailureResponse() throws Exception {
// relay the message first
var from = new BTPAddress(linkNet, sampleAddress.toString());
// byte[] data = requestMap.get(srcSn).getData();
byte[] data = "sendCallMessageWithRollback".getBytes();
var from = new BTPAddress(linkNet, sampleAddress);
byte[] data = requestMap.get(srcSn).getData();
var request = new CSMessageRequest(from.account(), fakeTo.account(), srcSn, true, data);
var csMsg = new CSMessage(CSMessage.REQUEST, request.toBytes());
MockBMCIntegrationTest.mockBMC.handleBTPMessage(
Expand Down

0 comments on commit 959681d

Please sign in to comment.