From 51cb0572eff213aaed7ed2fee53756a76d553850 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Date: Thu, 25 May 2023 14:07:35 +0200 Subject: [PATCH] Kernel/SDK: introduce PublicKey encoding --- src/kernel_sdk/CHANGES.rst | 1 + src/kernel_sdk/encoding/README.md | 2 +- src/kernel_sdk/encoding/src/lib.rs | 3 + src/kernel_sdk/encoding/src/mod.rs | 2 + src/kernel_sdk/encoding/src/public_key.rs | 185 ++++++++++++++++++++++ src/kernel_sdk/sdk/src/lib.rs | 4 +- 6 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 src/kernel_sdk/encoding/src/public_key.rs diff --git a/src/kernel_sdk/CHANGES.rst b/src/kernel_sdk/CHANGES.rst index 40cd19b5da3b..df00eb342e5c 100644 --- a/src/kernel_sdk/CHANGES.rst +++ b/src/kernel_sdk/CHANGES.rst @@ -57,6 +57,7 @@ SDK - Add a feature flag ``proto-nairobi`` to enable host functions introduced in the ``Nairobi`` protocol. - Implement host function ``store_delete_value`` introduced in the ``Nairobi`` protocol. +- Introduce ``PublicKey`` definition in ``tezos-smart-rollup-encoding`` Installer client ---------------- diff --git a/src/kernel_sdk/encoding/README.md b/src/kernel_sdk/encoding/README.md index a3d14ef4d202..e79e10c1bfd0 100644 --- a/src/kernel_sdk/encoding/README.md +++ b/src/kernel_sdk/encoding/README.md @@ -5,4 +5,4 @@ Included are types for: - parsing *inbox messages* from L1, including `Michelson` types. - constructing *outbox messages* to be executed on L1. - parsing & revealing the *Data Availability Committee* reveal tree encoding. -- tezos' `Contract` & `PublicKeyHash` types. +- tezos' `Contract` & `PublicKeyHash` & `PublicKey` types. diff --git a/src/kernel_sdk/encoding/src/lib.rs b/src/kernel_sdk/encoding/src/lib.rs index 5cb8afbcaccf..1fb82e64f015 100644 --- a/src/kernel_sdk/encoding/src/lib.rs +++ b/src/kernel_sdk/encoding/src/lib.rs @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2022-2023 TriliTech +// SPDX-FileCopyrightText: 2023 Marigold // // SPDX-License-Identifier: MIT @@ -26,6 +27,8 @@ pub mod michelson; #[cfg(feature = "alloc")] pub mod outbox; #[cfg(feature = "crypto")] +pub mod public_key; +#[cfg(feature = "crypto")] pub mod public_key_hash; #[cfg(feature = "crypto")] diff --git a/src/kernel_sdk/encoding/src/mod.rs b/src/kernel_sdk/encoding/src/mod.rs index 496e16e0666e..c4490187d40a 100644 --- a/src/kernel_sdk/encoding/src/mod.rs +++ b/src/kernel_sdk/encoding/src/mod.rs @@ -1,5 +1,6 @@ // SPDX-FileCopyrightText: 2022 TriliTech // SPDX-FileCopyrightText: 2022 Nomadic Labs +// SPDX-FileCopyrightText: 2023 Marigold // // SPDX-License-Identifier: MIT @@ -45,6 +46,7 @@ pub mod contract; pub mod entrypoint; pub mod micheline; pub mod michelson; +pub mod public_key; pub mod public_key_hash; pub mod smart_rollup; pub mod ticket; diff --git a/src/kernel_sdk/encoding/src/public_key.rs b/src/kernel_sdk/encoding/src/public_key.rs new file mode 100644 index 000000000000..16e06156a743 --- /dev/null +++ b/src/kernel_sdk/encoding/src/public_key.rs @@ -0,0 +1,185 @@ +// SPDX-FileCopyrightText: 2023 Marigold +// +// SPDX-License-Identifier: MIT + +//! Public Key of Layer1. + +use std::fmt::Display; +use tezos_crypto_rs::hash::{PublicKeyEd25519, PublicKeyP256, PublicKeySecp256k1}; +use tezos_data_encoding::enc::BinWriter; +use tezos_data_encoding::encoding::HasEncoding; +use tezos_data_encoding::nom::NomReader; + +use crypto::base58::{FromBase58Check, FromBase58CheckError}; +use crypto::hash::{Hash, HashTrait, HashType}; + +/// Public Key of Layer1. +#[derive(Debug, Clone, PartialEq, Eq, HasEncoding, BinWriter, NomReader)] +pub enum PublicKey { + /// Tz1 - public key + Ed25519(PublicKeyEd25519), + /// Tz2 - public key + Secp256k1(PublicKeySecp256k1), + /// Tz3 - public key + P256(PublicKeyP256), +} + +impl Display for PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Ed25519(tz1) => write!(f, "{}", tz1), + Self::Secp256k1(tz2) => write!(f, "{}", tz2), + Self::P256(tz3) => write!(f, "{}", tz3), + } + } +} + +impl PublicKey { + /// Conversion from base58-encoding string (with prefix). + pub fn from_b58check(data: &str) -> Result { + let bytes = data.from_base58check()?; + let public_key = if bytes + .starts_with(HashType::PublicKeyEd25519.base58check_prefix()) + { + PublicKey::Ed25519(PublicKeyEd25519::from_b58check(data)?) + } else if bytes.starts_with(HashType::PublicKeySecp256k1.base58check_prefix()) { + PublicKey::Secp256k1(PublicKeySecp256k1::from_b58check(data)?) + } else if bytes.starts_with(HashType::PublicKeyP256.base58check_prefix()) { + PublicKey::P256(PublicKeyP256::from_b58check(data)?) + } else { + return Err(FromBase58CheckError::InvalidBase58); + }; + Ok(public_key) + } + + /// Conversion to base58-encoding string (with prefix). + pub fn to_b58check(&self) -> String { + match self { + Self::Ed25519(tz1) => tz1.to_b58check(), + Self::Secp256k1(tz2) => tz2.to_b58check(), + Self::P256(tz3) => tz3.to_b58check(), + } + } +} + +impl From for Hash { + fn from(pkh: PublicKey) -> Self { + match pkh { + PublicKey::Ed25519(tz1) => tz1.into(), + PublicKey::Secp256k1(tz2) => tz2.into(), + PublicKey::P256(tz3) => tz3.into(), + } + } +} + +impl TryFrom<&str> for PublicKey { + type Error = FromBase58CheckError; + + fn try_from(value: &str) -> Result { + Self::from_b58check(value) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn tz1_b58check() { + let tz1 = "edpkuDMUm7Y53wp4gxeLBXuiAhXZrLn8XB1R83ksvvesH8Lp8bmCfK"; + + let pkh = PublicKey::from_b58check(tz1); + + assert!(matches!(pkh, Ok(PublicKey::Ed25519(_)))); + + let tz1_from_pkh = pkh.unwrap().to_b58check(); + + assert_eq!(tz1, &tz1_from_pkh); + } + + #[test] + fn tz2_b58check() { + let tz2 = "sppk7Zik17H7AxECMggqD1FyXUQdrGRFtz9X7aR8W2BhaJoWwSnPEGA"; + + let public_key = PublicKey::from_b58check(tz2); + + assert!(matches!(public_key, Ok(PublicKey::Secp256k1(_)))); + + let tz2_from_pk = public_key.unwrap().to_b58check(); + + assert_eq!(tz2, &tz2_from_pk); + } + + #[test] + fn tz3_b58check() { + let tz3 = "p2pk67VpBjWwoPULwXCpayec6rFxaAKv8VjJ8cVMHmLDCYARu31zx5Z"; + + let public_key = PublicKey::from_b58check(tz3); + + assert!(matches!(public_key, Ok(PublicKey::P256(_)))); + + let tz3_from_pk = public_key.unwrap().to_b58check(); + + assert_eq!(tz3, &tz3_from_pk); + } + + #[test] + fn tz1_encoding() { + let tz1 = "edpkuDMUm7Y53wp4gxeLBXuiAhXZrLn8XB1R83ksvvesH8Lp8bmCfK"; + + let public_key = PublicKey::from_b58check(tz1).expect("expected valid tz1 hash"); + + let mut bin = Vec::new(); + public_key + .bin_write(&mut bin) + .expect("serialization should work"); + + let deserde_pk = NomReader::nom_read(bin.as_slice()) + .expect("deserialization should work") + .1; + + // Check tag encoding + assert_eq!(0_u8, bin[0]); + assert_eq!(public_key, deserde_pk); + } + + #[test] + fn tz2_encoding() { + let tz2 = "sppk7Zik17H7AxECMggqD1FyXUQdrGRFtz9X7aR8W2BhaJoWwSnPEGA"; + + let public_key = PublicKey::from_b58check(tz2).expect("expected valid tz2 hash"); + + let mut bin = Vec::new(); + public_key + .bin_write(&mut bin) + .expect("serialization should work"); + + let deserde_pk = NomReader::nom_read(bin.as_slice()) + .expect("deserialization should work") + .1; + + // Check tag encoding + assert_eq!(1_u8, bin[0]); + assert_eq!(public_key, deserde_pk); + } + + #[test] + fn tz3_encoding() { + let tz3 = "p2pk67VpBjWwoPULwXCpayec6rFxaAKv8VjJ8cVMHmLDCYARu31zx5Z"; + + let public_key = PublicKey::from_b58check(tz3).expect("expected valid tz3 hash"); + + let mut bin = Vec::new(); + public_key + .bin_write(&mut bin) + .expect("serialization should work"); + + let deserde_pk = NomReader::nom_read(bin.as_slice()) + .expect("deserialization should work") + .1; + + // Check tag encoding + assert_eq!(2_u8, bin[0]); + assert_eq!(public_key, deserde_pk); + } +} diff --git a/src/kernel_sdk/sdk/src/lib.rs b/src/kernel_sdk/sdk/src/lib.rs index 2306960e8dc6..aa5ec73322c9 100644 --- a/src/kernel_sdk/sdk/src/lib.rs +++ b/src/kernel_sdk/sdk/src/lib.rs @@ -68,8 +68,8 @@ pub mod types { pub use tezos_smart_rollup_encoding::{ contract::Contract, entrypoint::Entrypoint, entrypoint::EntrypointError, - public_key_hash::PublicKeyHash, smart_rollup::SmartRollupAddress, - timestamp::Timestamp, + public_key::PublicKey, public_key_hash::PublicKeyHash, + smart_rollup::SmartRollupAddress, timestamp::Timestamp, }; pub use tezos_smart_rollup_host::input::Message; pub use tezos_smart_rollup_host::metadata::RollupMetadata; -- GitLab