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
// Copyright 2019 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Collections of helpers for synchronization with the Bitcoin network.

use async_trait::async_trait;
use bitcoincore_rpc::RpcApi;
use jsonrpc::Error as JsonRpcError;

use crate::btc;

/// Status of the transaction in the Bitcoin network.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum TransactionStatus {
    /// Transaction is unknown in the Bitcoin network.
    Unknown,
    /// The transaction is not committed, but presented in the Bitcoin node memory pool.
    Mempool,
    /// The transaction was completed to the Bitcoin blockchain with the specified number
    /// of confirmations.
    Committed(u32),
}

impl TransactionStatus {
    /// Checks that this transaction is known by the Bitcoin network.
    pub fn is_known(self) -> bool {
        self != TransactionStatus::Unknown
    }

    /// Returns number of transaction confirmations in Bitcoin blockchain.
    pub fn confirmations(self) -> Option<u32> {
        if let TransactionStatus::Committed(confirmations) = self {
            Some(confirmations)
        } else {
            None
        }
    }
}

/// Describes communication with the Bitcoin network node.
#[async_trait]
pub trait BitcoinRelay {
    /// Error type for the current Bitcoin relay implementation.
    type Error;
    /// Sends a raw transaction to the Bitcoin network node.
    async fn send_transaction(
        &self,
        transaction: &btc::Transaction,
    ) -> Result<btc::Sha256d, Self::Error>;
    /// Gets status for the transaction with the specified identifier.
    async fn transaction_status(&self, id: btc::Sha256d) -> Result<TransactionStatus, Self::Error>;
}

#[async_trait]
impl BitcoinRelay for bitcoincore_rpc::Client {
    type Error = bitcoincore_rpc::Error;

    async fn send_transaction(
        &self,
        transaction: &btc::Transaction,
    ) -> Result<btc::Sha256d, Self::Error> {
        self.send_raw_transaction(transaction.to_string())
            .map(|txid| btc::Sha256d(txid.into()))
    }

    async fn transaction_status(&self, id: btc::Sha256d) -> Result<TransactionStatus, Self::Error> {
        match self.get_raw_transaction_verbose(&id.into(), None) {
            Ok(info) => {
                let status = match info.confirmations {
                    None => TransactionStatus::Mempool,
                    Some(num) => TransactionStatus::Committed(num),
                };
                Ok(status)
            }
            // TODO Write more graceful error handling. [ECR-3222]
            Err(bitcoincore_rpc::Error::JsonRpc(JsonRpcError::Rpc(_))) => {
                Ok(TransactionStatus::Unknown)
            }
            Err(e) => Err(e),
        }
    }
}