bc/
pubkeys.rs

1// Bitcoin protocol consensus library.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Written in 2019-2024 by
6//     Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
7//
8// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved.
9//
10// Licensed under the Apache License, Version 2.0 (the "License");
11// you may not use this file except in compliance with the License.
12// You may obtain a copy of the License at
13//
14//     http://www.apache.org/licenses/LICENSE-2.0
15//
16// Unless required by applicable law or agreed to in writing, software
17// distributed under the License is distributed on an "AS IS" BASIS,
18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// See the License for the specific language governing permissions and
20// limitations under the License.
21
22use std::io;
23use std::str::FromStr;
24
25use amplify::hex::FromHex;
26use amplify::{hex, Bytes, Wrapper};
27use secp256k1::PublicKey;
28use strict_encoding::{
29    DecodeError, ReadStruct, ReadTuple, StrictDecode, StrictEncode, TypedRead, TypedWrite,
30    WriteStruct,
31};
32use strict_types::{StrictDeserialize, StrictSerialize};
33
34use crate::LIB_NAME_BITCOIN;
35
36#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
37#[display(doc_comments)]
38pub enum PubkeyParseError<const LEN: usize> {
39    #[from]
40    Hex(hex::Error),
41    #[from]
42    InvalidPubkey(InvalidPubkey<LEN>),
43}
44
45#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, From, Error)]
46pub enum InvalidPubkey<const LEN: usize> {
47    #[from(secp256k1::Error)]
48    #[display("invalid public key")]
49    Unspecified,
50
51    #[from]
52    #[display("invalid public key {0:x}")]
53    Specified(Bytes<LEN>),
54}
55
56#[derive(Wrapper, WrapperMut, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
57#[wrapper(Deref, LowerHex, Display)]
58#[wrapper_mut(DerefMut)]
59#[derive(StrictType, StrictDumb)]
60#[strict_type(lib = LIB_NAME_BITCOIN, dumb = Self::dumb())]
61#[cfg_attr(
62    feature = "serde",
63    derive(Serialize, Deserialize),
64    serde(crate = "serde_crate", transparent)
65)]
66pub struct CompressedPk(PublicKey);
67
68impl StrictSerialize for CompressedPk {}
69impl StrictDeserialize for CompressedPk {}
70
71impl CompressedPk {
72    fn dumb() -> Self { Self(PublicKey::from_slice(&[2u8; 33]).unwrap()) }
73
74    pub fn from_byte_array(data: [u8; 33]) -> Result<Self, InvalidPubkey<33>> {
75        PublicKey::from_slice(&data).map(Self).map_err(|_| InvalidPubkey::Specified(data.into()))
76    }
77    pub fn to_byte_array(&self) -> [u8; 33] { self.0.serialize() }
78
79    pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, InvalidPubkey<33>> {
80        Ok(CompressedPk(PublicKey::from_slice(bytes.as_ref())?))
81    }
82}
83
84impl StrictEncode for CompressedPk {
85    fn strict_encode<W: TypedWrite>(&self, writer: W) -> io::Result<W> {
86        let bytes = Bytes::<33>::from(self.0.serialize());
87        writer.write_newtype::<Self>(&bytes)
88    }
89}
90
91impl StrictDecode for CompressedPk {
92    fn strict_decode(reader: &mut impl TypedRead) -> Result<Self, DecodeError> {
93        reader.read_tuple(|r| {
94            let bytes: Bytes<33> = r.read_field()?;
95            PublicKey::from_slice(bytes.as_slice())
96                .map(Self)
97                .map_err(|_| InvalidPubkey::Specified(bytes).into())
98        })
99    }
100}
101
102impl FromStr for CompressedPk {
103    type Err = PubkeyParseError<33>;
104
105    fn from_str(s: &str) -> Result<Self, Self::Err> {
106        let data = <[u8; 33]>::from_hex(s)?;
107        let pk = Self::from_byte_array(data)?;
108        Ok(pk)
109    }
110}
111
112#[derive(Wrapper, WrapperMut, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
113#[wrapper(Deref, LowerHex, Display)]
114#[wrapper_mut(DerefMut)]
115#[derive(StrictType, StrictDumb)]
116#[strict_type(lib = LIB_NAME_BITCOIN, dumb = Self::dumb())]
117#[cfg_attr(
118    feature = "serde",
119    derive(Serialize, Deserialize),
120    serde(crate = "serde_crate", transparent)
121)]
122pub struct UncompressedPk(PublicKey);
123
124impl UncompressedPk {
125    fn dumb() -> Self { Self(PublicKey::from_slice(&[2u8; 33]).unwrap()) }
126
127    pub fn from_byte_array(data: [u8; 65]) -> Result<Self, InvalidPubkey<65>> {
128        PublicKey::from_slice(&data).map(Self).map_err(|_| InvalidPubkey::Specified(data.into()))
129    }
130    pub fn to_byte_array(&self) -> [u8; 65] { self.0.serialize_uncompressed() }
131}
132
133impl StrictEncode for UncompressedPk {
134    fn strict_encode<W: TypedWrite>(&self, writer: W) -> io::Result<W> {
135        let bytes = Bytes::<65>::from(self.0.serialize_uncompressed());
136        writer.write_newtype::<Self>(&bytes)
137    }
138}
139
140impl StrictDecode for UncompressedPk {
141    fn strict_decode(reader: &mut impl TypedRead) -> Result<Self, DecodeError> {
142        reader.read_tuple(|r| {
143            let bytes: Bytes<65> = r.read_field()?;
144            PublicKey::from_slice(bytes.as_slice())
145                .map(Self)
146                .map_err(|_| InvalidPubkey::Specified(bytes).into())
147        })
148    }
149}
150
151impl FromStr for UncompressedPk {
152    type Err = PubkeyParseError<65>;
153
154    fn from_str(s: &str) -> Result<Self, Self::Err> {
155        let data = <[u8; 65]>::from_hex(s)?;
156        let pk = Self::from_byte_array(data)?;
157        Ok(pk)
158    }
159}
160
161#[derive(Wrapper, WrapperMut, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
162#[wrapper(Deref, LowerHex, Display)]
163#[wrapper_mut(DerefMut)]
164#[derive(StrictType, StrictDumb)]
165#[strict_type(lib = LIB_NAME_BITCOIN, dumb = Self::dumb())]
166#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))]
167pub struct LegacyPk {
168    pub compressed: bool,
169    #[wrap]
170    pub pubkey: PublicKey,
171}
172
173impl From<PublicKey> for LegacyPk {
174    fn from(pk: PublicKey) -> Self { LegacyPk::compressed(pk) }
175}
176
177impl From<CompressedPk> for LegacyPk {
178    fn from(pk: CompressedPk) -> Self { LegacyPk::compressed(pk.0) }
179}
180
181impl From<UncompressedPk> for LegacyPk {
182    fn from(pk: UncompressedPk) -> Self { LegacyPk::uncompressed(pk.0) }
183}
184
185impl LegacyPk {
186    fn dumb() -> Self { Self::compressed(PublicKey::from_slice(&[2u8; 33]).unwrap()) }
187
188    pub const fn compressed(pubkey: PublicKey) -> Self {
189        LegacyPk {
190            compressed: true,
191            pubkey,
192        }
193    }
194
195    pub const fn uncompressed(pubkey: PublicKey) -> Self {
196        LegacyPk {
197            compressed: false,
198            pubkey,
199        }
200    }
201
202    pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, InvalidPubkey<65>> {
203        let bytes = bytes.as_ref();
204        let pubkey = PublicKey::from_slice(bytes)?;
205        Ok(match bytes.len() {
206            33 => Self::compressed(pubkey),
207            65 => Self::uncompressed(pubkey),
208            _ => unreachable!(),
209        })
210    }
211
212    pub fn to_vec(&self) -> Vec<u8> {
213        match self.compressed {
214            true => self.pubkey.serialize().to_vec(),
215            false => self.pubkey.serialize_uncompressed().to_vec(),
216        }
217    }
218}
219
220impl StrictEncode for LegacyPk {
221    fn strict_encode<W: TypedWrite>(&self, writer: W) -> io::Result<W> {
222        writer.write_struct::<Self>(|w| {
223            let bytes = Bytes::<33>::from(self.pubkey.serialize());
224            Ok(w.write_field(fname!("compressed"), &self.compressed)?
225                .write_field(fname!("pubkey"), &bytes)?
226                .complete())
227        })
228    }
229}
230
231impl StrictDecode for LegacyPk {
232    fn strict_decode(reader: &mut impl TypedRead) -> Result<Self, DecodeError> {
233        reader.read_struct(|r| {
234            let compressed = r.read_field(fname!("compressed"))?;
235            let bytes: Bytes<33> = r.read_field(fname!("pubkey"))?;
236            let pubkey = PublicKey::from_slice(bytes.as_slice())
237                .map_err(|_| InvalidPubkey::Specified(bytes))?;
238            Ok(LegacyPk { compressed, pubkey })
239        })
240    }
241}
242
243impl FromStr for LegacyPk {
244    type Err = PubkeyParseError<65>;
245
246    fn from_str(s: &str) -> Result<Self, Self::Err> {
247        let data = Vec::<u8>::from_hex(s)?;
248        let pk = Self::from_bytes(data)?;
249        Ok(pk)
250    }
251}