1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
use flex_error::define_error;
use ibc::core::ics02_client::error::Error as Ics02Error;
use ibc::core::ics24_host::identifier::{ChainId, ChannelId, PortId};
use ibc::events::IbcEvent;
use ibc::Height;

use crate::channel::ChannelError;
use crate::connection::ConnectionError;
use crate::error::Error;
use crate::foreign_client::{ForeignClientError, HasExpiredOrFrozenError};
use crate::supervisor::Error as SupervisorError;
use crate::transfer::TransferError;

define_error! {
    LinkError {
        Relayer
            [ Error ]
            |_| { "link failed with underlying error" },

        Supervisor
            [ SupervisorError ]
            |_| { "error originating from the supervisor" },

        Initialization
            [ ChannelError ]
            |_| { "link initialization failed during channel counterparty verification" },

        PacketProofsConstructor
            { chain_id: ChainId }
            [ Error ]
            |e| {
                format!("failed to construct packet proofs for chain {0}", e.chain_id)
            },

        Query
            { chain_id: ChainId }
            [ Error ]
            |e| {
                format!("failed during query to chain id {0}", e.chain_id)
            },

        Channel
            [ ChannelError ]
            |_| { "channel error" },

        ChannelNotFound
            {
                port_id: PortId,
                channel_id: ChannelId,
                chain_id: ChainId,
            }
            [ Error ]
            |e| {
                format!("channel {}/{} does not exist on chain {}",
                    e.port_id, e.channel_id, e.chain_id)
            },

        Connection
            [ ConnectionError ]
            |_| { "connection error" },

        Client
            [ ForeignClientError ]
            |_| { "failed during a client operation" },

        Packet
            [ TransferError ]
            |_| { "packet error" },

        OldPacketClearingFailed
            |_| { "clearing of old packets failed" },

        Send
            { event: IbcEvent }
            |e| {
                format!("chain error when sending messages: {0}", e.event)
            },

        MissingChannelId
            { chain_id: ChainId }
            |e| {
                format!("missing channel_id on chain {}", e.chain_id)
            },

        Signer
            {
                chain_id: ChainId
            }
            [ Error ]
            |e| {
                format!("could not retrieve signer from src chain {}", e.chain_id)
            },

        DecrementHeight
            { height: Height }
            [ Ics02Error ]
            |e| {
                format!("Cannot clear packets @height {}, because this height cannot be decremented", e.height)
            },

        UnexpectedEvent
            { event: IbcEvent }
            |e| {
                format!("unexpected query tx response: {}", e.event)
            },

        UpdateClientEventNotFound
            | _ | { "update client event not found in tx response" },

        InvalidChannelState
            {
                channel_id: ChannelId,
                chain_id: ChainId,
            }
            |e| {
                format!("channel {} on chain {} not in open or close state when packets and timeouts can be relayed",
                    e.channel_id, e.chain_id)
            },

        ChannelNotOpened
            {
                channel_id: ChannelId,
                chain_id: ChainId,
            }
            |e| {
                format!("connection for channel {} on chain {} is not in open state",
                    e.channel_id, e.chain_id)
            },

        CounterpartyChannelNotFound
            {
                channel_id: ChannelId,
            }
            |e| {
                format!("counterparty channel id not found for {}",
                    e.channel_id)
            },

        NoConnectionHop
            {
                channel_id: ChannelId,
                chain_id: ChainId,
            }
            |e| {
                format!("channel {} on chain {} has no connection hops",
                    e.channel_id, e.chain_id)
            },

        UpdateClientFailed
             |_| { "failed to update client" },
   }
}

impl HasExpiredOrFrozenError for LinkErrorDetail {
    fn is_expired_or_frozen_error(&self) -> bool {
        match self {
            Self::Client(e) => e.source.is_expired_or_frozen_error(),
            _ => false,
        }
    }
}

impl HasExpiredOrFrozenError for LinkError {
    fn is_expired_or_frozen_error(&self) -> bool {
        self.detail().is_expired_or_frozen_error()
    }
}