subcoin_runtime_primitives/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2// TODO: This originates from `sp_api::decl_runtime_apis`.
3#![allow(clippy::multiple_bound_locations)]
4
5extern crate alloc;
6
7use alloc::vec::Vec;
8use bitcoin::consensus::Encodable;
9use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
10use scale_info::TypeInfo;
11use sp_core::H256;
12use sp_runtime::ConsensusEngineId;
13
14/// The `ConsensusEngineId` of Bitcoin block hash.
15pub const NAKAMOTO_HASH_ENGINE_ID: ConsensusEngineId = *b"hash";
16
17/// The `ConsensusEngineId` of Bitcoin block header.
18pub const NAKAMOTO_HEADER_ENGINE_ID: ConsensusEngineId = *b"hedr";
19
20/// 1 BTC in satoshis
21const COIN: u64 = 100_000_000;
22
23/// Initial block reward
24const INITIAL_SUBSIDY: u64 = 50 * COIN;
25
26const HALVING_INTERVAL: u32 = 210_000;
27
28const MAX_SCRIPT_SIZE: usize = 10_000;
29
30/// Unspent transaction output.
31#[derive(Debug, Clone, PartialEq, Eq, TypeInfo, Encode, Decode)]
32#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
33pub struct Coin {
34    /// Whether the coin is from a coinbase transaction.
35    pub is_coinbase: bool,
36    /// Transfer value in satoshis.
37    pub amount: u64,
38    // Block height at which this containing transaction was included.
39    pub height: u32,
40    /// Spending condition of the output.
41    pub script_pubkey: Vec<u8>,
42}
43
44impl MaxEncodedLen for Coin {
45    fn max_encoded_len() -> usize {
46        bool::max_encoded_len() + u64::max_encoded_len() + MAX_SCRIPT_SIZE
47    }
48}
49
50/// Wrapper type for representing [`bitcoin::Txid`] in runtime, stored in reversed byte order.
51#[derive(
52    Debug,
53    Clone,
54    Copy,
55    TypeInfo,
56    Encode,
57    Decode,
58    DecodeWithMemTracking,
59    MaxEncodedLen,
60    PartialEq,
61    Eq,
62)]
63pub struct Txid(H256);
64
65impl From<bitcoin::Txid> for Txid {
66    fn from(txid: bitcoin::Txid) -> Self {
67        let mut bytes = Vec::with_capacity(32);
68        txid.consensus_encode(&mut bytes)
69            .expect("txid must be encoded correctly; qed");
70
71        bytes.reverse();
72
73        let bytes: [u8; 32] = bytes
74            .try_into()
75            .expect("Bitcoin txid is sha256 hash which must fit into [u8; 32]; qed");
76
77        Self(H256::from(bytes))
78    }
79}
80
81impl From<Txid> for bitcoin::Txid {
82    fn from(txid: Txid) -> Self {
83        let mut bytes = txid.encode();
84        bytes.reverse();
85        bitcoin::consensus::Decodable::consensus_decode(&mut bytes.as_slice())
86            .expect("Decode must succeed as txid was guaranteed to be encoded correctly; qed")
87    }
88}
89
90/// A reference to a transaction output.
91#[derive(
92    Debug, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, MaxEncodedLen,
93)]
94pub struct OutPoint {
95    /// The transaction ID of the referenced output.
96    pub txid: Txid,
97    /// The index of the output within the referenced transaction.
98    pub vout: u32,
99}
100
101impl From<bitcoin::OutPoint> for OutPoint {
102    fn from(out_point: bitcoin::OutPoint) -> Self {
103        Self {
104            txid: out_point.txid.into(),
105            vout: out_point.vout,
106        }
107    }
108}
109
110impl From<OutPoint> for bitcoin::OutPoint {
111    fn from(val: OutPoint) -> Self {
112        bitcoin::OutPoint {
113            txid: val.txid.into(),
114            vout: val.vout,
115        }
116    }
117}
118
119/// Returns the amount of subsidy in satoshis at given height.
120pub fn bitcoin_block_subsidy(height: u32) -> u64 {
121    block_subsidy(height, HALVING_INTERVAL)
122}
123
124/// Returns the block subsidy at given height and halving interval.
125fn block_subsidy(height: u32, subsidy_halving_interval: u32) -> u64 {
126    let halvings = height / subsidy_halving_interval;
127    // Force block reward to zero when right shift is undefined.
128    if halvings >= 64 {
129        return 0;
130    }
131
132    let mut subsidy = INITIAL_SUBSIDY;
133
134    // Subsidy is cut in half every 210,000 blocks which will occur
135    // approximately every 4 years.
136    subsidy >>= halvings;
137
138    subsidy
139}
140
141sp_api::decl_runtime_apis! {
142    /// Subcoin API.
143    pub trait SubcoinApi {
144        /// Same as the original `execute_block()` with the removal
145        /// of `state_root` check in the `final_checks()`.
146        fn execute_block_without_state_root_check(block: Block);
147
148        /// Finalize block without checking the extrinsics_root and state_root.
149        fn finalize_block_without_checks(header: Block::Header);
150
151        /// Returns the number of total coins (i.e., the size of UTXO set).
152        fn coins_count() -> u64;
153
154        /// Query UTXOs by outpoints.
155        ///
156        /// Returns a vector of the same length as the input, with `Some(Coin)` for
157        /// UTXOs that exist and `None` for those that don't.
158        fn get_utxos(outpoints: Vec<OutPoint>) -> Vec<Option<Coin>>;
159    }
160}