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
use cumulus_primitives_core::relay_chain::AccountId;
18
use snowbridge_core::ChannelId;
19
use snowbridge_inbound_queue_primitives::v2;
20
use {
21
    alloc::vec::Vec,
22
    frame_support::pallet_prelude::*,
23
    parity_scale_codec::DecodeAll,
24
    snowbridge_core::{Channel, PRIMARY_GOVERNANCE_CHANNEL},
25
    snowbridge_inbound_queue_primitives::{
26
        v1::{Envelope, MessageProcessor},
27
        v2::MessageProcessorError,
28
    },
29
    sp_runtime::DispatchError,
30
};
31

            
32
/// Magic bytes are added in every payload intended for this processor to make sure
33
/// that we are the intended recipient of the message. Reason being scale encoding is not type aware.
34
/// So a same set of bytes can be decoded for two different data structures if their
35
/// total size is same. Magic bytes can be checked after decoding to make sure that the sender
36
/// indeed send a message intended for this processor.
37
pub const MAGIC_BYTES: [u8; 4] = [112, 21, 0, 56];
38

            
39
/// Payload is the whole data we expect to receive from the relayer
40
#[derive(Encode, Decode, DebugNoBound)]
41
pub struct Payload<T>
42
where
43
    T: pallet_external_validators::Config,
44
{
45
    pub magic_bytes: [u8; 4],
46
    pub message: Message<T>,
47
}
48

            
49
/// Actual message inside the payload
50
#[derive(Encode, Decode, DebugNoBound)]
51
pub enum Message<T>
52
where
53
    T: pallet_external_validators::Config,
54
{
55
    V1(InboundCommand<T>),
56
}
57

            
58
/// Command to be executed by this message processor
59
#[derive(Encode, Decode, DebugNoBound)]
60
pub enum InboundCommand<T>
61
where
62
    T: pallet_external_validators::Config,
63
{
64
    ReceiveValidators {
65
        validators: Vec<<T as pallet_external_validators::Config>::ValidatorId>,
66
        external_index: u64,
67
    },
68
}
69

            
70
pub struct SymbioticMessageProcessor<T>(PhantomData<T>);
71

            
72
impl<T> SymbioticMessageProcessor<T>
73
where
74
    T: pallet_external_validators::Config,
75
{
76
53
    fn can_process_message(mut payload: &[u8]) -> bool {
77
53
        let decode_result = Payload::<T>::decode_all(&mut payload);
78
53
        match decode_result {
79
12
            Ok(payload) => {
80
12
                if payload.magic_bytes == MAGIC_BYTES {
81
10
                    true
82
                } else {
83
2
                    log::debug!("SymbioticMessageProcessor: magic number mismatch, will try next processor: {:?}", payload.magic_bytes);
84
2
                    false
85
                }
86
            }
87
41
            Err(e) => {
88
                // Message cannot be decoded as `Payload`.
89
                // This is expected if the message is intended for a different processor.
90
41
                log::trace!("SymbioticMessageProcessor: failed to decode payload. This is expected if the message is not for this processor. Error: {:?}", e);
91
41
                false
92
            }
93
        }
94
53
    }
95

            
96
12
    fn process_message(
97
12
        channel_id: Option<ChannelId>,
98
12
        mut payload: &[u8],
99
12
    ) -> Result<(), DispatchError> {
100
12
        let decode_result = Payload::<T>::decode_all(&mut payload);
101
12
        let message = if let Ok(payload) = decode_result {
102
10
            payload.message
103
        } else {
104
2
            return Err(DispatchError::Other("unable to parse the envelope payload"));
105
        };
106

            
107
10
        log::trace!("SymbioticMessageProcessor: {:?}", message);
108

            
109
10
        match message {
110
            Message::V1(InboundCommand::ReceiveValidators {
111
10
                validators,
112
10
                external_index,
113
            }) => {
114
                // Process message for v2 does not contain a channel-id whatsoever.
115
                // We could possibly check the origin as v2 has an origin, or convert the origin into a channle or viceversa
116
                // TODO for the inbound queue people
117
10
                if let Some(channel_id) = channel_id {
118
10
                    if channel_id != PRIMARY_GOVERNANCE_CHANNEL {
119
2
                        return Err(DispatchError::Other(
120
2
                            "Received governance message from invalid channel id",
121
2
                        ));
122
8
                    }
123
                }
124
8
                pallet_external_validators::Pallet::<T>::set_external_validators_inner(
125
8
                    validators,
126
8
                    external_index,
127
                )?;
128
8
                Ok(())
129
            }
130
        }
131
12
    }
132
}
133

            
134
impl<T> v2::MessageProcessor<AccountId> for SymbioticMessageProcessor<T>
135
where
136
    T: pallet_external_validators::Config,
137
{
138
    fn can_process_message(_who: &AccountId, message: &v2::Message) -> bool {
139
        match &message.payload {
140
            v2::message::Payload::Raw(data) => Self::can_process_message(&data),
141
            v2::message::Payload::CreateAsset { .. } => false,
142
        }
143
    }
144

            
145
    fn process_message(
146
        _who: AccountId,
147
        message: v2::Message,
148
    ) -> Result<[u8; 32], MessageProcessorError> {
149
        match &message.payload {
150
            v2::message::Payload::Raw(data) => Self::process_message(None, &data)
151
                .map(|_| [0; 32])
152
                .map_err(|e| MessageProcessorError::ProcessMessage(e)),
153
            v2::message::Payload::CreateAsset { .. } => Err(MessageProcessorError::ProcessMessage(
154
                DispatchError::Other("Create asset is not supported"),
155
            )),
156
        }
157
    }
158
}
159

            
160
impl<T> MessageProcessor for SymbioticMessageProcessor<T>
161
where
162
    T: pallet_external_validators::Config,
163
{
164
53
    fn can_process_message(_channel: &Channel, envelope: &Envelope) -> bool {
165
53
        Self::can_process_message(&envelope.payload)
166
53
    }
167

            
168
12
    fn process_message(_channel: Channel, envelope: Envelope) -> Result<(), DispatchError> {
169
12
        Self::process_message(Some(envelope.channel_id), &envelope.payload)
170
12
    }
171
}