Skip to content

Commit 06d5969

Browse files
OffRamp: handle execution state for message on merkleroot (#139)
* handle execution state for message on merkleroot * fix compilation and add events * fix errors not being correctly thrown and asserted * reduce merkleRootId to fit on bounced message * test that the offRamp emits event when message bounces * define constants for the execution states on bindings * refactor Logs.ts to not use ternary logig for LogMatch * fmt * use crc32 on opcodes * refactor handlers to their own functions on contracts, add receiver and rootId fields to messages * fix errors on interfaces using rootId * refactor: handlers for contract messages into funcitons * remove CCIP prefix from ExecutionStateChanged * fix typo
1 parent 374a5ff commit 06d5969

File tree

7 files changed

+487
-217
lines changed

7 files changed

+487
-217
lines changed

contracts/contracts/ccip/merkle_root.tolk

Lines changed: 93 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,15 @@ import "../lib/utils.tolk";
88
const STATE_UNTOUCHED: uint8 = 0;
99
const STATE_TOKEN_TRANSFER: uint8 = 1;
1010
const STATE_EXECUTE: uint8 = 2;
11-
const STATE_SUCCESS: uint8 = 3;
12-
13-
// Execution state stores CCIP specfic information
14-
// These have to match the EVM states
15-
const EXECUTION_STATE_UNTOUCHED: uint8 = 0;
16-
const EXECUTION_STATE_IN_PROGRESS: uint8 = 1;
17-
const EXECUTION_STATE_SUCCESS: uint8 = 2;
18-
const EXECUTION_STATE_FAILURE: uint8 = 3;
11+
const STATE_EXECUTE_FAILED: uint8 = 3;
12+
const STATE_SUCCESS: uint8 = 4;
13+
1914
// TODO: we need to fit additional states: TOKEN_TRANSFER | EXECUTE
2015

21-
const ERROR_STATE_IS_NOT_UNTOUCHED: uint32 = 0x7878;
16+
const ERROR_STATE_IS_NOT_UNTOUCHED = 300;
17+
const ERROR_UPDATING_STATE_OF_NON_EXECUTED_MESSAGE = 301;
18+
const ERROR_NOTIFICATION_FROM_INVALID_RECEIVER = 303;
19+
2220

2321
fun MerkleRoot_Storage.load(): MerkleRoot_Storage {
2422
return MerkleRoot_Storage.fromCell(contract.getData());
@@ -28,66 +26,126 @@ fun MerkleRoot_Storage.store(self) {
2826
return contract.setData(self.toCell());
2927
}
3028

31-
32-
type MerkleRoot_InMessage = MerkleRoot_Validate;
29+
type MerkleRoot_InMessage = MerkleRoot_Validate | MerkleRoot_CCIPReceiveBounced | MerkleRoot_CCIPReceiveConfirm;
3330

3431
fun onInternalMessage(in: InMessage) {
3532
val msg = lazy MerkleRoot_InMessage.fromSlice(in.body);
3633
match (msg) {
3734
MerkleRoot_Validate => {
3835
// If we receive this message from the OffRamp then the merkle proof has already been validated.
3936
_validateAndExecute(msg, in.senderAddress);
40-
37+
}
38+
MerkleRoot_CCIPReceiveConfirm => {
39+
_ccipReceiveConfirm(msg, in.senderAddress);
40+
}
41+
MerkleRoot_CCIPReceiveBounced => {
42+
_ccipReceiveBounced(msg, in.senderAddress);
4143
}
4244
else => {
4345
// ignore empty messages, "wrong opcode" for others
4446
assert (in.body.isEmpty()) throw 0xFFFF
4547
}
4648
}
4749
}
50+
fun _ccipReceiveBounced(msg: MerkleRoot_CCIPReceiveBounced, sender: address){
51+
var st = MerkleRoot_Storage.load();
52+
53+
assert(sender == st.owner, ERROR_NOT_OWNER);
54+
assert(st.state == STATE_EXECUTE && st.message != null, ERROR_UPDATING_STATE_OF_NON_EXECUTED_MESSAGE);
55+
56+
val ccipMessage = st.message!.load();
57+
58+
assert (msg.receiver == ccipMessage.receiver, ERROR_NOTIFICATION_FROM_INVALID_RECEIVER);
59+
60+
st.state = STATE_EXECUTE_FAILED;
61+
st.store();
62+
63+
val messageHeader = ccipMessage.header;
64+
val offRampNotifyFailure = createMessage({
65+
bounce: true,
66+
value: ton("0"),
67+
dest: st.owner,
68+
body: OffRamp_NotifyFailure {
69+
header: messageHeader,
70+
rootId: st.rootId,
71+
},
72+
});
73+
offRampNotifyFailure.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE);
74+
75+
}
4876

49-
//TODO: We are not keeping track of the execution state right now, we just relay the message to confirm that the merkle proof is valid
77+
fun _ccipReceiveConfirm(msg: MerkleRoot_CCIPReceiveConfirm, sender: address) {
78+
var st = MerkleRoot_Storage.load();
79+
80+
assert(st.state == STATE_EXECUTE && st.message != null, ERROR_UPDATING_STATE_OF_NON_EXECUTED_MESSAGE);
81+
assert(sender == st.owner, ERROR_NOT_OWNER);
82+
83+
val ccipMessage = st.message!.load();
84+
85+
assert (msg.receiver == ccipMessage.receiver, ERROR_NOTIFICATION_FROM_INVALID_RECEIVER);
86+
87+
st.state = STATE_SUCCESS;
88+
st.store();
89+
90+
val messageHeader = ccipMessage.header;
91+
val offRampNotifySuccess = createMessage({
92+
bounce: true,
93+
value: ton("0"),
94+
dest: st.owner,
95+
body: OffRamp_NotifySuccess {
96+
header: messageHeader,
97+
rootId: st.rootId
98+
},
99+
});
100+
offRampNotifySuccess.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE);
101+
}
50102

51103
fun _validateAndExecute(msg: MerkleRoot_Validate, sender: address) {
52-
val st = MerkleRoot_Storage.load();
104+
var st = MerkleRoot_Storage.load();
105+
53106
assert(sender == st.owner, ERROR_NOT_OWNER);
107+
assert(st.state == STATE_UNTOUCHED, ERROR_STATE_IS_NOT_UNTOUCHED);
54108

55-
assert(st.state == EXECUTION_STATE_UNTOUCHED, ERROR_STATE_IS_NOT_UNTOUCHED);
56-
// TODO if no tokenTransfers
57-
_execute(st, msg.messages, msg.proofs, msg.proofFlagBits, msg.metadataHash, sender);
58-
59-
// if tokenTransfers
60-
// validate execution state == untouched
61-
// if initial
62-
// allocate storage space for the execution report
63-
// TODO: should we fan out CommitReport into individual executions?
64-
//
65-
// if in progress
109+
val ccipMessage = msg.message;
110+
111+
st.message = ccipMessage.toCell();
112+
st.store();
113+
114+
//TODO
66115
// if tokenAmounts > 0 && state == initial
116+
// val notifyInProgress = createMessage({
117+
// bounce: true,
118+
// value: ton("0.05"),
119+
// dest: st.owner,
120+
// body: OffRamp_NotifyInProgress {
121+
// st.messages, msg.proofs, msg.proofFlagBits, msg.metadataHash
122+
// }
123+
// });
124+
// notifyInProgress.send(SEND_MODE_REGULAR);
67125
// set state = token transfer
68126
// send message to offramp to ask to release tokens
127+
69128
// else if no tokenTransfers
70-
// set state = execution
129+
_execute(st, ccipMessage);
71130
}
72131

73-
fun _execute(st: MerkleRoot_Storage, mesages: cell, proofs: cell, proofFlagBits: uint256, metadataHash: uint256, offRamp: address) {
74-
st.state = STATE_SUCCESS;
132+
fun _execute(st: MerkleRoot_Storage, mesage: Any2TVMRampMessage) {
133+
st.state = STATE_EXECUTE;
75134
st.store();
76135
createMessage(
77136
{
78137
bounce: true,
79-
value: ton("0"),
80-
dest: offRamp,
138+
value: ton("0.05"),
139+
dest: st.owner,
81140
body: OffRamp_DispatchValidated {
82-
messages: mesages,
83-
proofs: proofs,
84-
proofFlagBits: proofFlagBits,
85-
metadataHash: metadataHash
141+
message: mesage.toCell(),
142+
rootId: st.rootId,
86143
}
87144
}
88-
).send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE);
145+
).send(SEND_MODE_REGULAR);
89146
}
90147

148+
91149
// TODO: should we wait for all transfers to either complete or fail before we allow retry?
92150

93151
// fun onExcesses() {

0 commit comments

Comments
 (0)