1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Tanssi is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
16

            
17
extern crate alloc;
18

            
19
use crate::processors::v2::fallback_message_processor::{
20
    AssetTrapFallbackProcessor, PrivilegedFallbackProcessor,
21
};
22
use crate::processors::v2::{
23
    CodecError, FallbackMessageProcessor, MessageExtractionError, MessageProcessorWithFallback,
24
};
25
use alloc::boxed::Box;
26
use alloc::format;
27
use alloc::string::ToString;
28
use alloy_core::sol_types::SolType;
29
use core::marker::PhantomData;
30
use cumulus_primitives_core::Weight;
31
use parity_scale_codec::Decode;
32
use snowbridge_inbound_queue_primitives::v2::{Message, MessageProcessorError, Payload};
33
use sp_core::{Get, H160};
34
use tp_bridge::layerzero_message::{
35
    InboundMessage as LayerZeroInboundMessage, InboundSolMessage as LayerZeroInboundSolMessage,
36
};
37
use v2_processor_proc_macro::MessageProcessor;
38
use xcm::latest::{ExecuteXcm, InteriorLocation, NetworkId};
39
use xcm_executor::traits::WeightBounds;
40

            
41
7
pub fn try_extract_message<T: pallet_lz_router::Config>(
42
7
    message: &Message,
43
7
    gateway_proxy_address: H160,
44
7
) -> Result<LayerZeroInboundMessage, MessageExtractionError> {
45
7
    match message.payload {
46
7
        Payload::Raw(ref payload) => {
47
7
            let raw_payload = crate::processors::v2::RawPayload::decode(&mut payload.as_slice())
48
7
                .map_err(|error| MessageExtractionError::InvalidMessage {
49
1
                    context: "Unable to decode RawMessage".to_string(),
50
1
                    source: Some(Box::new(CodecError(error))),
51
1
                })?;
52
6
            match raw_payload {
53
5
                crate::processors::v2::RawPayload::LayerZero(payload) => {
54
5
                    if message.origin != gateway_proxy_address {
55
1
                        return Err(MessageExtractionError::InvalidMessage {
56
1
                            context: format!(
57
1
                                "LayerZero message origin is {:?} expected {:?}",
58
1
                                message.origin, gateway_proxy_address
59
1
                            ),
60
1
                            source: None,
61
1
                        });
62
4
                    }
63

            
64
4
                    if message.value > 0 || !message.assets.is_empty() {
65
2
                        return Err(MessageExtractionError::InvalidMessage {
66
2
                            context: "LayerZero message cannot have assets".to_string(),
67
2
                            source: None,
68
2
                        });
69
2
                    }
70

            
71
1
                    let sol_message =
72
2
                        LayerZeroInboundSolMessage::abi_decode_validate(&mut payload.as_slice())
73
2
                            .map_err(|error| MessageExtractionError::InvalidMessage {
74
1
                                context: "Unable to decode LayerZero Payload".to_string(),
75
1
                                source: Some(Box::new(error)),
76
1
                            })?;
77

            
78
1
                    sol_message
79
1
                        .try_into()
80
1
                        .map_err(|e| MessageExtractionError::InvalidMessage {
81
                            context: format!("LayerZero message conversion failed: {}", e),
82
                            source: None,
83
                        })
84
                }
85
1
                _ => Err(MessageExtractionError::UnsupportedMessage {
86
1
                    context: "Unsupported Message".to_string(),
87
1
                    source: None,
88
1
                }),
89
            }
90
        }
91
        _ => Err(MessageExtractionError::UnsupportedMessage {
92
            context: "Unsupported Message".to_string(),
93
            source: None,
94
        }),
95
    }
96
7
}
97

            
98
pub fn process_message<T: pallet_lz_router::Config>(
99
    message: LayerZeroInboundMessage,
100
) -> Result<(), MessageProcessorError> {
101
    pallet_lz_router::Pallet::<T>::handle_inbound_message(message)
102
}
103

            
104
#[derive(MessageProcessor)]
105
pub struct LayerZeroMessageProcessor<
106
    T,
107
    GatewayAddress,
108
    DefaultClaimer,
109
    EthereumNetwork,
110
    EthereumUniversalLocation,
111
    TanssiUniversalLocation,
112
    XcmProcessor,
113
    XcmWeigher,
114
>(
115
    PhantomData<(
116
        T,
117
        GatewayAddress,
118
        DefaultClaimer,
119
        EthereumNetwork,
120
        EthereumUniversalLocation,
121
        TanssiUniversalLocation,
122
        XcmProcessor,
123
        XcmWeigher,
124
    )>,
125
);
126

            
127
impl<
128
        T,
129
        AccountId,
130
        GatewayAddress,
131
        DefaultClaimer,
132
        EthereumNetwork,
133
        EthereumUniversalLocation,
134
        TanssiUniversalLocation,
135
        XcmProcessor,
136
        XcmWeigher,
137
    > MessageProcessorWithFallback<AccountId>
138
    for LayerZeroMessageProcessor<
139
        T,
140
        GatewayAddress,
141
        DefaultClaimer,
142
        EthereumNetwork,
143
        EthereumUniversalLocation,
144
        TanssiUniversalLocation,
145
        XcmProcessor,
146
        XcmWeigher,
147
    >
148
where
149
    T: snowbridge_pallet_inbound_queue::Config
150
        + pallet_xcm::Config
151
        + snowbridge_pallet_system::Config
152
        + pallet_lz_router::Config,
153
    [u8; 32]: From<<T as frame_system::Config>::AccountId>,
154
    GatewayAddress: Get<H160>,
155
    DefaultClaimer: Get<<T as frame_system::Config>::AccountId>,
156
    EthereumNetwork: Get<NetworkId>,
157
    EthereumUniversalLocation: Get<InteriorLocation>,
158
    TanssiUniversalLocation: Get<InteriorLocation>,
159
    XcmProcessor: ExecuteXcm<<T as pallet_xcm::Config>::RuntimeCall>,
160
    XcmWeigher: WeightBounds<<T as pallet_xcm::Config>::RuntimeCall>,
161
{
162
    type Fallback = PrivilegedFallbackProcessor<
163
        T,
164
        AssetTrapFallbackProcessor<
165
            T,
166
            GatewayAddress,
167
            DefaultClaimer,
168
            EthereumNetwork,
169
            EthereumUniversalLocation,
170
            TanssiUniversalLocation,
171
            XcmProcessor,
172
            XcmWeigher,
173
        >,
174
        GatewayAddress,
175
        DefaultClaimer,
176
        EthereumNetwork,
177
        EthereumUniversalLocation,
178
        TanssiUniversalLocation,
179
        XcmProcessor,
180
        XcmWeigher,
181
    >;
182
    type ExtractedMessage = LayerZeroInboundMessage;
183

            
184
7
    fn try_extract_message(
185
7
        _sender: &AccountId,
186
7
        message: &Message,
187
7
    ) -> Result<Self::ExtractedMessage, MessageExtractionError> {
188
7
        let gateway_proxy_address = T::GatewayAddress::get();
189
7
        try_extract_message::<T>(message, gateway_proxy_address)
190
7
    }
191

            
192
    fn process_extracted_message(
193
        _sender: AccountId,
194
        extracted_message: Self::ExtractedMessage,
195
    ) -> Result<Option<Weight>, MessageProcessorError> {
196
        process_message::<T>(extracted_message).map(|_| None)
197
    }
198
}