Skip to main content

ckb_transaction_firewall_sdk/
types.rs

1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4#[cfg(feature = "serde")]
5mod serde_pubkeys_33 {
6    use serde::de::{Error, SeqAccess, Visitor};
7    use serde::ser::SerializeSeq;
8    use serde::{Deserializer, Serializer};
9    use std::fmt;
10
11    pub fn serialize<S>(pubkeys: &Vec<[u8; 33]>, serializer: S) -> Result<S::Ok, S::Error>
12    where
13        S: Serializer,
14    {
15        let mut seq = serializer.serialize_seq(Some(pubkeys.len()))?;
16        for pubkey in pubkeys {
17            seq.serialize_element(&pubkey.as_slice())?;
18        }
19        seq.end()
20    }
21
22    pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<[u8; 33]>, D::Error>
23    where
24        D: Deserializer<'de>,
25    {
26        struct PubkeysVisitor;
27
28        impl<'de> Visitor<'de> for PubkeysVisitor {
29            type Value = Vec<[u8; 33]>;
30
31            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
32                formatter.write_str("a sequence of 33-byte public keys")
33            }
34
35            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
36            where
37                A: SeqAccess<'de>,
38            {
39                let mut pubkeys = Vec::with_capacity(seq.size_hint().unwrap_or(0));
40                while let Some(pubkey) = seq.next_element::<Vec<u8>>()? {
41                    if pubkey.len() != 33 {
42                        return Err(A::Error::custom("public key must be exactly 33 bytes"));
43                    }
44                    let mut out = [0u8; 33];
45                    out.copy_from_slice(&pubkey);
46                    pubkeys.push(out);
47                }
48                Ok(pubkeys)
49            }
50        }
51
52        deserializer.deserialize_seq(PubkeysVisitor)
53    }
54}
55
56/// CKB script hash type.
57#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
58#[derive(Debug, Clone, PartialEq, Eq)]
59#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
60pub enum HashType {
61    Data,
62    Type,
63    Data1,
64}
65
66impl HashType {
67    /// Serialises to the single-byte on-chain encoding: data=0, type=1, data1=2.
68    pub fn to_byte(&self) -> u8 {
69        match self {
70            HashType::Data => 0,
71            HashType::Type => 1,
72            HashType::Data1 => 2,
73        }
74    }
75
76    /// Deserialises from the single-byte on-chain encoding. Returns `None` for
77    /// any byte that is not a known hash type.
78    pub fn from_byte(b: u8) -> Option<Self> {
79        match b {
80            0 => Some(HashType::Data),
81            1 => Some(HashType::Type),
82            2 => Some(HashType::Data1),
83            _ => None,
84        }
85    }
86}
87
88/// A CKB script (lock or type) in its component form.
89#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
90#[derive(Debug, Clone, PartialEq, Eq)]
91pub struct ScriptLike {
92    pub code_hash: [u8; 32],
93    pub hash_type: HashType,
94    pub args: Vec<u8>,
95}
96
97/// Identifies a registry cell dep by the 32-byte Type ID value stored at
98/// bytes 34–66 of the v2 registry type-script args.
99///
100/// Matching on `type_id_value` rather than full type-script equality means the
101/// spec survives governance-lock upgrades: the governance code hash at bytes
102/// 1–33 can change without invalidating this spec.
103///
104/// Mirrors `RegistrySpecLike` in the TypeScript SDK.
105#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
106#[derive(Debug, Clone, PartialEq, Eq)]
107pub struct RegistrySpec {
108    pub code_hash: [u8; 32],
109    pub hash_type: HashType,
110    /// Bytes 34–66 of the registry cell's type-script args (the Type ID value).
111    pub type_id_value: [u8; 32],
112    /// When `true`, absence of a matching cell dep is an error.
113    /// When `false`, the spec is silently skipped if no dep is found.
114    pub required: bool,
115}
116
117/// A minimal view of a CKB cell dependency as seen by the preflight checker.
118#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
119#[derive(Debug, Clone)]
120pub struct CellDepLike {
121    pub type_script: Option<ScriptLike>,
122    pub data: Vec<u8>,
123}
124
125/// A minimal view of a CKB transaction output as seen by the preflight checker.
126#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
127#[derive(Debug, Clone)]
128pub struct TxOutputLike {
129    pub lock_args: Vec<u8>,
130    pub type_args: Option<Vec<u8>>,
131}
132
133/// A minimal unsigned CKB transaction as required by `check_transaction`.
134#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
135#[derive(Debug, Clone)]
136pub struct UnsignedTxLike {
137    pub cell_deps: Vec<CellDepLike>,
138    pub outputs: Vec<TxOutputLike>,
139}
140
141/// Configuration for [`check_transaction`](crate::check_transaction).
142///
143/// Mirrors `FirewallConfig` in the TypeScript SDK.
144#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
145#[derive(Debug, Clone)]
146pub struct FirewallConfig {
147    pub registries: Vec<RegistrySpec>,
148}
149
150/// A single blacklist entry: an identifier (arbitrary bytes) and an expiry
151/// timestamp in Unix seconds. `expires_at == 0` means the entry never expires.
152#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
153#[derive(Debug, Clone)]
154pub struct RegistryEntry {
155    pub identifier: Vec<u8>,
156    /// Unix seconds. `0` means the entry is permanent.
157    pub expires_at: u64,
158}
159
160/// Governance header embedded in every BLKL v2 registry payload.
161#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
162#[derive(Debug, Clone)]
163pub struct GovernanceHeader {
164    pub signer_count: u8,
165    pub threshold: u8,
166    #[cfg_attr(feature = "serde", serde(with = "serde_pubkeys_33"))]
167    pub pubkeys: Vec<[u8; 33]>,
168    pub validator_count: u16,
169    pub validator_merkle_root: [u8; 32],
170}
171
172/// A parsed BLKL v2 registry payload.
173#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
174#[derive(Debug, Clone)]
175pub struct RegistryPayload {
176    pub version: u8,
177    pub entries: Vec<RegistryEntry>,
178    pub governance_header: Option<GovernanceHeader>,
179}
180
181// ── Builder types ─────────────────────────────────────────────────────────────
182
183/// On-chain location of a deployed cell.
184#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
185#[derive(Debug, Clone, PartialEq, Eq)]
186pub struct OutPointLike {
187    pub tx_hash: [u8; 32],
188    pub index: u32,
189}
190
191/// A cell dependency entry for inclusion in a CKB transaction.
192#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
193#[derive(Debug, Clone)]
194pub struct TransactionCellDep {
195    pub out_point: OutPointLike,
196    pub dep_type: DepType,
197}
198
199/// The dependency type of a cell dep.
200#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
201#[derive(Debug, Clone, PartialEq, Eq)]
202#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
203pub enum DepType {
204    Code,
205    DepGroup,
206}
207
208/// Everything needed to assemble the cell deps for spending a firewall-protected
209/// cell. Registry outpoints move after each governance update — always fetch
210/// fresh values before calling [`build_firewall_spend_cell_deps`](crate::build_firewall_spend_cell_deps).
211#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
212#[derive(Debug, Clone)]
213pub struct FirewallSpendDepsConfig {
214    /// Deployed firewall-lock code cell (the RISC-V binary).
215    pub firewall_lock_out_point: OutPointLike,
216    /// Deployed inner lock code cell (e.g. spawn-aware-secp256k1).
217    pub inner_lock_out_point: OutPointLike,
218    /// Live registry data cells — one per registry spec in the lock args.
219    pub registry_out_points: Vec<OutPointLike>,
220}
221
222/// Full configuration for building a v2 firewall lock script.
223///
224/// Encodes to the v2 `FirewallLockArgs` byte layout:
225/// `version(1)=0x02 | flags(1) | registry_count(1) |
226/// [code_hash(32)|hash_type(1)|type_id_value(32)|required(1)]×N |
227/// inner_code_hash(32) | inner_hash_type(1) | inner_args_len(2 LE) | inner_args(M)`
228#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
229#[derive(Debug, Clone)]
230pub struct FirewallLockConfig {
231    /// Code hash and hash type of the deployed firewall-lock script.
232    pub firewall_code_hash: [u8; 32],
233    pub firewall_hash_type: HashType,
234    /// Check flags: bit 0 = check lock_args, bit 1 = check type_args.
235    /// At least one bit must be set; reserved bits (0xfc) must be clear.
236    pub flags: u8,
237    /// Registry specs to enforce. Maximum 255 entries.
238    pub registries: Vec<RegistrySpec>,
239    /// The inner lock to delegate to after blacklist checking passes.
240    pub inner_code_hash: [u8; 32],
241    pub inner_hash_type: HashType,
242    /// Args for the inner lock. Maximum 65535 bytes.
243    pub inner_args: Vec<u8>,
244}