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 {
18
    fc_rpc::frontier_backend_client::{self, is_canon},
19
    jsonrpsee::{core::RpcResult, proc_macros::rpc},
20
    sp_blockchain::HeaderBackend,
21
    sp_core::H256,
22
    sp_runtime::traits::Block,
23
    std::{marker::PhantomData, sync::Arc},
24
};
25

            
26
336
#[rpc(server)]
27
#[async_trait::async_trait]
28
pub trait FrontierFinalityApi {
29
    /// Reports whether a Substrate or Ethereum block is finalized.
30
    /// Returns false if the block is not found.
31
    #[method(name = "frnt_isBlockFinalized")]
32
    async fn is_block_finalized(&self, block_hash: H256) -> RpcResult<bool>;
33

            
34
    /// Reports whether an Ethereum transaction is finalized.
35
    /// Returns false if the transaction is not found
36
    #[method(name = "frnt_isTxFinalized")]
37
    async fn is_tx_finalized(&self, tx_hash: H256) -> RpcResult<bool>;
38
}
39

            
40
pub struct FrontierFinality<B: Block, C> {
41
    pub backend: Arc<dyn fc_api::Backend<B>>,
42
    pub client: Arc<C>,
43
    _phdata: PhantomData<B>,
44
}
45

            
46
impl<B: Block, C> FrontierFinality<B, C> {
47
296
    pub fn new(client: Arc<C>, backend: Arc<dyn fc_api::Backend<B>>) -> Self {
48
296
        Self {
49
296
            backend,
50
296
            client,
51
296
            _phdata: Default::default(),
52
296
        }
53
296
    }
54
}
55

            
56
#[async_trait::async_trait]
57
impl<B, C> FrontierFinalityApiServer for FrontierFinality<B, C>
58
where
59
    B: Block<Hash = H256>,
60
    C: HeaderBackend<B> + Send + Sync + 'static,
61
{
62
10
    async fn is_block_finalized(&self, raw_hash: H256) -> RpcResult<bool> {
63
10
        let client = self.client.clone();
64
10
        is_block_finalized_inner::<B, C>(self.backend.as_ref(), &client, raw_hash).await
65
20
    }
66

            
67
10
    async fn is_tx_finalized(&self, tx_hash: H256) -> RpcResult<bool> {
68
10
        let client = self.client.clone();
69

            
70
8
        if let Some((ethereum_block_hash, _ethereum_index)) =
71
10
            frontier_backend_client::load_transactions::<B, C>(
72
10
                &client,
73
10
                self.backend.as_ref(),
74
10
                tx_hash,
75
10
                true,
76
10
            )
77
            .await?
78
        {
79
8
            is_block_finalized_inner::<B, C>(self.backend.as_ref(), &client, ethereum_block_hash)
80
                .await
81
        } else {
82
2
            Ok(false)
83
        }
84
20
    }
85
}
86

            
87
18
async fn is_block_finalized_inner<B: Block<Hash = H256>, C: HeaderBackend<B> + 'static>(
88
18
    backend: &(dyn fc_api::Backend<B>),
89
18
    client: &C,
90
18
    raw_hash: H256,
91
18
) -> RpcResult<bool> {
92
18
    let substrate_hash =
93
18
        match frontier_backend_client::load_hash::<B, C>(client, backend, raw_hash).await? {
94
            // If we find this hash in the frontier data base, we know it is an eth hash
95
8
            Some(hash) => hash,
96
            // Otherwise, we assume this is a Substrate hash.
97
10
            None => raw_hash,
98
        };
99

            
100
    // First check whether the block is in the best chain
101
18
    if !is_canon(client, substrate_hash) {
102
2
        return Ok(false);
103
16
    }
104
16

            
105
16
    // At this point we know the block in question is in the current best chain.
106
16
    // It's just a question of whether it is in the finalized prefix or not
107
16
    let query_height = client
108
16
        .number(substrate_hash)
109
16
        .expect("No sp_blockchain::Error should be thrown when looking up hash")
110
16
        .expect("Block is already known to be canon, so it must be in the chain");
111
16
    let finalized_height = client.info().finalized_number;
112
16

            
113
16
    Ok(query_height <= finalized_height)
114
18
}