ckb_cli_plugin_protocol/
lib.rs

1mod convert;
2mod jsonrpc;
3pub mod method;
4
5use std::fmt;
6use std::str::FromStr;
7
8use ckb_jsonrpc_types::{BlockView, HeaderView, JsonBytes, Script, Transaction};
9use ckb_types::{H160, H256};
10use serde_derive::{Deserialize, Serialize};
11
12pub use jsonrpc::{
13    CellIndex, JsonrpcError, JsonrpcRequest, JsonrpcResponse, LiveCellInfo, JSONRPC_VERSION,
14};
15
16#[derive(Clone, Debug, Serialize, Deserialize)]
17pub struct PluginConfig {
18    pub name: String,
19    pub description: String,
20    pub daemon: bool,
21    pub roles: Vec<PluginRole>,
22}
23
24impl PluginConfig {
25    pub fn validate(&self) -> Result<(), String> {
26        // TODO: validate PluginConfig.name
27        if self.roles.is_empty() {
28            return Err(String::from("Role list can not be empty"));
29        }
30        for role in &self.roles {
31            role.validate()?;
32        }
33        Ok(())
34    }
35
36    pub fn is_normal_daemon(&self) -> bool {
37        if !self.daemon {
38            return false;
39        }
40        for role in &self.roles {
41            match role {
42                PluginRole::KeyStore { .. } => (),
43                PluginRole::Indexer => (),
44                _ => {
45                    return true;
46                }
47            }
48        }
49        false
50    }
51}
52
53#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
54#[serde(tag = "role", rename_all = "snake_case")]
55pub enum PluginRole {
56    // The argument is for if keystore need password
57    KeyStore { require_password: bool },
58    Indexer,
59    // The argument is for where the sub-command is injected to.
60    SubCommand { name: String },
61    // The argument is for the callback function name
62    Callback { name: CallbackName },
63}
64
65impl PluginRole {
66    pub fn validate(&self) -> Result<(), String> {
67        match self {
68            Self::SubCommand { .. } => {
69                // TODO: check sub-command name
70                Ok(())
71            }
72            _ => Ok(()),
73        }
74    }
75}
76
77#[derive(Serialize, Deserialize, Debug, Clone)]
78pub enum PluginRequest {
79    // == Send from ckb-cli to plugin
80    // Tell a daemon plugin to quit
81    Quit,
82    GetConfig,
83    // Notify all daemon plugins and indexer when rpc url changed
84    RpcUrlChanged(String),
85    // The plugin need to parse the rest command line arguments
86    SubCommand(String),
87    Callback(CallbackRequest),
88    // == Send from plugin to ckb-cli
89    Rpc(RpcRequest),
90    ReadPassword(String),
91    PrintStdout(String),
92    PrintStderr(String),
93    // == Can send from both direction
94    KeyStore(KeyStoreRequest),
95    Indexer {
96        genesis_hash: H256,
97        request: IndexerRequest,
98    },
99}
100
101#[derive(Serialize, Deserialize, Debug, Clone)]
102#[serde(tag = "type", rename_all = "snake_case", content = "content")]
103pub enum PluginResponse {
104    Error(JsonrpcError),
105    Ok,
106    // For get_config request
107    PluginConfig(PluginConfig),
108    JsonValue(serde_json::Value),
109    Boolean(bool),
110    String(String),
111    Integer64(u64),
112
113    H256Opt(Option<H256>),
114    H160(H160),
115    H160Vec(Vec<H160>),
116    HeaderView(Box<HeaderView>),
117    HeaderViewOpt(Box<Option<HeaderView>>),
118    BlockViewOpt(Box<Option<BlockView>>),
119    Bytes(JsonBytes),
120    BytesVec(Vec<JsonBytes>),
121
122    Callback(CallbackResponse),
123    MasterPrivateKey {
124        privkey: JsonBytes,
125        chain_code: JsonBytes,
126    },
127    DerivedKeySet {
128        external: Vec<(String, H160)>,
129        change: Vec<(String, H160)>,
130    },
131
132    LiveCells(Vec<LiveCellInfo>),
133    TopN(Vec<(H256, Option<Script>, u64)>),
134}
135
136#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq)]
137#[serde(rename_all = "snake_case")]
138pub enum CallbackName {
139    SendTransaction,
140}
141impl fmt::Display for CallbackName {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        let repr = match self {
144            CallbackName::SendTransaction => "send_transaction",
145        };
146        write!(f, "{}", repr)
147    }
148}
149#[derive(Serialize, Deserialize, Debug, Clone)]
150pub enum CallbackRequest {
151    SendTransaction {
152        tx: Transaction,
153        // Send in which subcommand: transfer/deposite/withdraw/prepare/tx
154        sub_command: String,
155    },
156    // TODO: add more
157}
158
159#[derive(Serialize, Deserialize, Debug, Clone)]
160#[serde(tag = "type", rename_all = "snake_case", content = "content")]
161pub enum CallbackResponse {
162    SendTransaction {
163        accepted: bool,
164        error_message: String,
165    },
166}
167
168#[derive(Serialize, Deserialize, Debug, Clone)]
169#[serde(tag = "type", rename_all = "snake_case", content = "content")]
170pub enum SignTarget {
171    Transaction {
172        tx: Transaction,
173        inputs: Vec<Transaction>,
174        change_path: String,
175    },
176    AnyMessage(H256),
177    AnyString(String),
178    AnyData(JsonBytes),
179}
180
181#[derive(Serialize, Deserialize, Debug, Clone)]
182pub enum KeyStoreRequest {
183    // return: PluginResponse::Bytes
184    ListAccount,
185    // return: PluginResponse::Boolean
186    HasAccount(H160),
187    // return: PluginResponse::H160
188    CreateAccount(Option<String>),
189    // return: PluginResponse::Ok
190    UpdatePassword {
191        hash160: H160,
192        password: String,
193        new_password: String,
194    },
195    // return: PluginResponse::H160
196    Import {
197        privkey: [u8; 32],
198        chain_code: [u8; 32],
199        password: Option<String>,
200    },
201    // return: PluginResponse::H160
202    ImportAccount {
203        account_id: JsonBytes,
204        password: Option<String>,
205    },
206    // return: PluginResponse::MasterPrivateKey
207    Export {
208        hash160: H160,
209        password: Option<String>,
210    },
211    // return: PluginResponse::Bytes
212    Sign {
213        hash160: H160,
214        path: String,
215        message: H256,
216        target: Box<SignTarget>,
217        recoverable: bool,
218        password: Option<String>,
219    },
220    // return: PluginResponse::Bytes
221    ExtendedPubkey {
222        hash160: H160,
223        path: String,
224        password: Option<String>,
225    },
226    // return: PluginResponse::DerivedKeySet
227    DerivedKeySet {
228        hash160: H160,
229        external_max_len: u32,
230        change_last: H160,
231        change_max_len: u32,
232        password: Option<String>,
233    },
234    // return: PluginResponse::DerivedKeySet
235    DerivedKeySetByIndex {
236        hash160: H160,
237        external_start: u32,
238        external_length: u32,
239        change_start: u32,
240        change_length: u32,
241        password: Option<String>,
242    },
243    // For plugin to use custom keystore
244    // return: PluginResponse::JsonValue
245    Any(serde_json::Value),
246}
247
248#[derive(Serialize, Deserialize, Debug, Clone)]
249pub enum RpcRequest {
250    GetBlock { hash: H256 },
251    GetBlockByNumber { number: u64 },
252    GetBlockHash { number: u64 },
253    // TODO: add more
254}
255
256#[derive(Serialize, Deserialize, Debug, Clone)]
257#[serde(rename_all = "snake_case")]
258pub enum LiveCellIndexType {
259    LockHash,
260    TypeHash,
261    // Code hash of type script
262    CodeHash,
263}
264impl fmt::Display for LiveCellIndexType {
265    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266        let repr = match self {
267            LiveCellIndexType::LockHash => "lock_hash",
268            LiveCellIndexType::TypeHash => "type_hash",
269            LiveCellIndexType::CodeHash => "code_hash",
270        };
271        write!(f, "{}", repr)
272    }
273}
274impl FromStr for LiveCellIndexType {
275    type Err = String;
276
277    fn from_str(s: &str) -> Result<Self, Self::Err> {
278        match s {
279            "lock_hash" => Ok(LiveCellIndexType::LockHash),
280            "type_hash" => Ok(LiveCellIndexType::TypeHash),
281            "code_hash" => Ok(LiveCellIndexType::CodeHash),
282            _ => Err(format!("Invalid index type: {}", s)),
283        }
284    }
285}
286#[derive(Serialize, Deserialize, Debug, Clone)]
287pub enum IndexerRequest {
288    TipHeader,
289    LastHeader,
290    // Get total capacity by lock hash
291    GetCapacity(H256),
292    LiveCells {
293        index: LiveCellIndexType,
294        hash: H256,
295        from_number: Option<u64>,
296        to_number: Option<u64>,
297        limit: u64,
298    },
299    TopN(u64),
300    IndexerInfo,
301    // For plugin to use custom indexer
302    Any(serde_json::Value),
303}