uckb_key/address/
mod.rs

1// Copyright (C) 2019-2020 Boyu Yang
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::{fmt, str};
10
11use bech32::{FromBase32, ToBase32, Variant};
12use property::Property;
13
14pub mod error;
15use error::{Error, Result};
16
17use crate::{blake2b, utilities};
18
19#[cfg(test)]
20mod tests;
21
22pub const CODE_HASH_SIZE: usize = 32;
23pub const BLAKE160_SIZE: usize = 20;
24pub const SINCE_SIZE: usize = 8;
25
26/// CKB Network
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum Network {
29    Main,
30    Test,
31}
32
33/// Payload Format Types
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum PayloadFormat {
36    Short,
37    Full(CodeHashType),
38}
39
40/// Code Hash Index
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum CodeHashIndex {
43    Secp256k1Blake160 = 0x00,
44    Secp256k1MultiSig = 0x01,
45}
46
47/// Code Hash Type
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum CodeHashType {
50    Data = 0x02,
51    Type = 0x04,
52}
53
54/// Code Hash
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum CodeHash {
57    Index(CodeHashIndex),
58    Data {
59        hash_type: CodeHashType,
60        content: [u8; CODE_HASH_SIZE],
61    },
62}
63
64/// Args
65#[derive(Debug, Clone)]
66pub enum Args {
67    Simple(Vec<u8>),
68    MultiSig {
69        version: u8,
70        first_n_required: u8,
71        threshold: u8,
72        contents: Vec<[u8; BLAKE160_SIZE]>,
73        since: Option<[u8; 8]>,
74    },
75}
76
77#[derive(Property, Clone)]
78#[property(get(public), set(disable), mut(disable))]
79pub struct Address {
80    network: Network,
81    code_hash: CodeHash,
82    args: Args,
83}
84
85#[derive(Property, Default, Clone)]
86#[property(get(disable), set(public, prefix = "", type = "own"), mut(disable))]
87pub struct AddressBuilder {
88    network: Network,
89    code_hash: CodeHash,
90    args: Args,
91}
92
93impl Default for Network {
94    fn default() -> Self {
95        Network::Main
96    }
97}
98
99impl Default for CodeHashIndex {
100    fn default() -> Self {
101        CodeHashIndex::Secp256k1Blake160
102    }
103}
104
105impl Default for CodeHash {
106    fn default() -> Self {
107        Self::Index(Default::default())
108    }
109}
110
111impl Default for Args {
112    fn default() -> Self {
113        Self::Simple(vec![0u8; BLAKE160_SIZE])
114    }
115}
116
117impl Network {
118    pub fn value(self) -> &'static str {
119        match self {
120            Self::Main => "ckb",
121            Self::Test => "ckt",
122        }
123    }
124
125    pub fn from_value(value: &str) -> Result<Self> {
126        match value {
127            "ckb" => Ok(Self::Main),
128            "ckt" => Ok(Self::Test),
129            v => Err(Error::UnknownNetwork(v.to_owned())),
130        }
131    }
132}
133
134impl fmt::Display for Network {
135    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136        let s = match self {
137            Self::Main => "mainnet",
138            Self::Test => "testnet",
139        };
140        write!(f, "{}", s)
141    }
142}
143
144impl PayloadFormat {
145    pub fn value(self) -> u8 {
146        if let Self::Full(t) = self {
147            t.value()
148        } else {
149            0x01 // Short
150        }
151    }
152
153    pub fn from_value(value: u8) -> Result<Self> {
154        match value {
155            0x01 => Ok(Self::Short),
156            0x02 => Ok(Self::Full(CodeHashType::Data)),
157            0x04 => Ok(Self::Full(CodeHashType::Type)),
158            v => Err(Error::UnknownPayloadFormat(v)),
159        }
160    }
161}
162
163impl CodeHashIndex {
164    pub fn value(self) -> u8 {
165        self as u8
166    }
167
168    pub fn from_value(value: u8) -> Result<Self> {
169        match value {
170            0x00 => Ok(Self::Secp256k1Blake160),
171            0x01 => Ok(Self::Secp256k1MultiSig),
172            v => Err(Error::UnknownCodeHashIndex(v)),
173        }
174    }
175}
176
177impl CodeHashType {
178    pub fn value(self) -> u8 {
179        self as u8
180    }
181}
182
183impl fmt::Display for CodeHash {
184    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185        match *self {
186            CodeHash::Index(index) => write!(f, "CodeHash::Index({:?})", index),
187            CodeHash::Data {
188                hash_type,
189                ref content,
190            } => write!(
191                f,
192                "CodeHash::Data {{ hash_type: {:?}, content: {} }}",
193                hash_type,
194                utilities::hex_string(&content[..])
195            ),
196        }
197    }
198}
199
200impl Args {
201    pub fn serialize_into(&self, buf: &mut Vec<u8>) {
202        match self {
203            Args::Simple(ref args) => {
204                buf.extend_from_slice(&args[..]);
205            }
206            Args::MultiSig {
207                version,
208                first_n_required,
209                threshold,
210                contents,
211                since,
212            } => {
213                let len = 4 + BLAKE160_SIZE * contents.len();
214                let mut bin = Vec::with_capacity(len);
215                bin.push(*version);
216                bin.push(*first_n_required);
217                bin.push(*threshold);
218                bin.push(contents.len() as u8);
219                for content in &contents[..] {
220                    bin.extend_from_slice(&content[..]);
221                }
222                let hash = blake2b::blake160(&bin);
223                buf.extend_from_slice(&hash[..]);
224                if let Some(since) = since {
225                    buf.extend_from_slice(&since[..]);
226                }
227            }
228        }
229    }
230}
231
232impl fmt::Display for Args {
233    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
234        write!(f, "Args {{")?;
235        match self {
236            Self::Simple(content) => {
237                write!(f, " Simple({})", utilities::hex_string(&content[..]))?;
238            }
239            Self::MultiSig {
240                version,
241                first_n_required,
242                threshold,
243                contents,
244                since,
245            } => {
246                write!(f, " MultiSig {{")?;
247                write!(f, " version: {}", version)?;
248                write!(f, " first_n: {}", first_n_required)?;
249                write!(f, " threshold: {}", threshold)?;
250                write!(f, " number: {}", contents.len())?;
251                let mut first = true;
252                write!(f, " contents: [")?;
253                for content in &contents[..] {
254                    if first {
255                        first = false;
256                    } else {
257                        write!(f, ", ")?;
258                    }
259                    write!(f, "{}", utilities::hex_string(&content[..]))?;
260                }
261                write!(f, "]")?;
262                if let Some(since) = since {
263                    write!(f, " since: {}", utilities::hex_string(&since[..]))?;
264                }
265                write!(f, " }}")?;
266            }
267        };
268        write!(f, " }}")
269    }
270}
271
272impl Address {
273    pub fn into_builder(self) -> AddressBuilder {
274        let Self {
275            network,
276            code_hash,
277            args,
278        } = self;
279        AddressBuilder {
280            network,
281            code_hash,
282            args,
283        }
284    }
285}
286
287impl fmt::Debug for Address {
288    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
289        write!(f, "Address {{")?;
290        write!(f, " network: {}", self.network)?;
291        write!(f, " , code_hash: {}", self.code_hash)?;
292        write!(f, " , args: {}", self.args)?;
293        write!(f, " }}")
294    }
295}
296
297impl fmt::Display for Address {
298    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
299        let hrp = self.network.value();
300        let data: Vec<u8> = match self.code_hash {
301            CodeHash::Index(index) => {
302                let mut data = Vec::with_capacity(2 + BLAKE160_SIZE);
303                data.push(PayloadFormat::Short.value());
304                data.push(index.value());
305                match index {
306                    CodeHashIndex::Secp256k1Blake160 => match self.args {
307                        Args::Simple(_) => {
308                            self.args.serialize_into(&mut data);
309                            Ok(data)
310                        }
311                        _ => Err(Error::Unreachable(
312                            "unsupported args for Secp256k1Blake160".to_owned(),
313                        )),
314                    },
315                    CodeHashIndex::Secp256k1MultiSig => match self.args {
316                        Args::Simple(_) => {
317                            self.args.serialize_into(&mut data);
318                            Ok(data)
319                        }
320                        Args::MultiSig { since, .. } => {
321                            if since.is_none() {
322                                self.args.serialize_into(&mut data);
323                                Ok(data)
324                            } else {
325                                Err(Error::Unreachable(
326                                    "since should be None for Secp256k1MultiSig in Short Format"
327                                        .to_owned(),
328                                ))
329                            }
330                        }
331                    },
332                }
333            }
334            CodeHash::Data {
335                hash_type,
336                ref content,
337            } => {
338                let args_len = match self.args {
339                    Args::Simple(ref args) => args.len(),
340                    Args::MultiSig { since, .. } => {
341                        BLAKE160_SIZE + since.map(|x| x.len()).unwrap_or(0)
342                    }
343                };
344                let mut data = Vec::with_capacity(1 + CODE_HASH_SIZE + content.len() + args_len);
345                data.push(PayloadFormat::Full(hash_type).value());
346                data.extend_from_slice(&content[..]);
347                self.args.serialize_into(&mut data);
348                Ok(data)
349            }
350        }
351        .unwrap();
352        bech32::encode_to_fmt(f, hrp, data.to_base32(), Variant::Bech32).unwrap()
353    }
354}
355
356impl str::FromStr for Address {
357    type Err = Error;
358    fn from_str(s: &str) -> Result<Self> {
359        bech32::decode(s)
360            .map_err(Error::Bech32)
361            .and_then(|(ref hrp, ref base32, variant)| {
362                if variant != Variant::Bech32 {
363                    return Err(Error::UnsupportedBech32Variant(variant));
364                }
365                let network = Network::from_value(hrp)?;
366                let bytes = Vec::<u8>::from_base32(base32).map_err(Error::Bech32)?;
367                let mut offset = 0;
368                let mut data = &bytes[offset..];
369                if data.is_empty() {
370                    Err(Error::InvalidDataSince(offset))
371                } else {
372                    let format = PayloadFormat::from_value(data[0])?;
373                    offset += 1;
374                    data = &bytes[offset..];
375                    let (code_hash, args) = match format {
376                        PayloadFormat::Short => {
377                            if data.is_empty() {
378                                return Err(Error::InvalidDataSince(offset));
379                            }
380                            let index = CodeHashIndex::from_value(data[0])?;
381                            offset += 1;
382                            data = &bytes[offset..];
383                            let args = if data.len() == BLAKE160_SIZE {
384                                Ok(Args::Simple(data.to_owned()))
385                            } else {
386                                Err(Error::ShortFormatArgs)
387                            }?;
388                            (CodeHash::Index(index), args)
389                        }
390                        PayloadFormat::Full(hash_type) => {
391                            if data.len() < CODE_HASH_SIZE {
392                                return Err(Error::InvalidDataSince(offset));
393                            }
394                            let mut content = [0u8; CODE_HASH_SIZE];
395                            content.copy_from_slice(&data[..CODE_HASH_SIZE]);
396                            offset += CODE_HASH_SIZE;
397                            data = &bytes[offset..];
398                            (
399                                CodeHash::Data { hash_type, content },
400                                Args::Simple(data.to_owned()),
401                            )
402                        }
403                    };
404                    AddressBuilder::default()
405                        .network(network)
406                        .code_hash(code_hash)
407                        .args(args)
408                        .build()
409                }
410            })
411    }
412}
413
414impl AddressBuilder {
415    pub fn new() -> Self {
416        Self::default()
417    }
418
419    pub fn code_hash_by_index(mut self, index: CodeHashIndex) -> Self {
420        self.code_hash = CodeHash::Index(index);
421        self
422    }
423
424    pub fn code_hash_by_data(
425        mut self,
426        hash_type: CodeHashType,
427        content: [u8; CODE_HASH_SIZE],
428    ) -> Self {
429        self.code_hash = CodeHash::Data { hash_type, content };
430        self
431    }
432
433    pub fn args_simple(mut self, args: Vec<u8>) -> Self {
434        self.args = Args::Simple(args);
435        self
436    }
437
438    pub fn args_multisig(
439        mut self,
440        version: u8,
441        first_n_required: u8,
442        threshold: u8,
443        contents: Vec<[u8; BLAKE160_SIZE]>,
444        since: Option<[u8; SINCE_SIZE]>,
445    ) -> Self {
446        self.args = Args::MultiSig {
447            version,
448            first_n_required,
449            threshold,
450            contents,
451            since,
452        };
453        self
454    }
455
456    pub fn build(self) -> Result<Address> {
457        let Self {
458            network,
459            code_hash,
460            args,
461        } = self;
462        match code_hash {
463            CodeHash::Index(index) => match index {
464                CodeHashIndex::Secp256k1Blake160 => match args {
465                    Args::Simple(ref content) => {
466                        if content.len() == BLAKE160_SIZE {
467                            Ok(())
468                        } else {
469                            Err(Error::ShortFormatArgs)
470                        }
471                    }
472                    _ => Err(Error::Secp256k1Blake160Args),
473                },
474                CodeHashIndex::Secp256k1MultiSig => match args {
475                    Args::Simple(ref content) => {
476                        if content.len() == BLAKE160_SIZE {
477                            Ok(())
478                        } else {
479                            Err(Error::ShortFormatArgs)
480                        }
481                    }
482                    Args::MultiSig { .. } => Ok(()),
483                },
484            },
485            CodeHash::Data { .. } => Ok(()),
486        }?;
487        if let Args::MultiSig {
488            first_n_required,
489            threshold,
490            ref contents,
491            ..
492        } = args
493        {
494            if first_n_required > threshold || threshold > contents.len() as u8 {
495                return Err(Error::MultiSigArgs);
496            }
497        };
498        Ok(Address {
499            network,
500            code_hash,
501            args,
502        })
503    }
504}