Skip to content
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ resolver = "2"
[workspace.dependencies]
bincode = { package = "grovedb-bincode", version = "=2.1.0" }
bincode_derive = { package = "grovedb-bincode-derive", version = "=2.1.0" }
dash-pkc = { git = "https://github.com/dashpay/base-sdk", rev = "e6402ced257c370a586ade9840ebbf545a4b7926", default-features = false }
dash-types = { git = "https://github.com/dashpay/base-sdk", rev = "e6402ced257c370a586ade9840ebbf545a4b7926", default-features = false }

Comment thread
ZocoLini marked this conversation as resolved.
[workspace.package]
version = "0.45.0"
Expand Down
8 changes: 5 additions & 3 deletions crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@ edition = "2021"
default = []

bincode = ["dep:bincode", "dep:bincode_derive", "dashcore_hashes/bincode", "dash-network/bincode"]
bls = ["dep:dash-pkc", "dep:tracing"]
bls = ["dep:dash-pkc", "dash-pkc/bls", "dep:tracing"]
eddsa = ["dep:dash-pkc", "dash-pkc/eddsa"]
serde = ["dep:serde", "dash-types/serde", "dashcore_hashes/serde", "dash-network/serde", "secp256k1/serde"]

[dependencies]
base58ck = { version = "0.1.0" }
bincode = { workspace = true, optional = true }
bincode_derive = { workspace = true, optional = true }
dash-network = { path = "../dash-network" }
dash-pkc = { version = "=0.1.0-beta", default-features = false, features = ["bls", "std"], optional = true }
dash-types = { version = "=0.1.0-beta", default-features = false, features = ["codec"] }
dash-pkc = { workspace = true, features = ["std"], optional = true }
dash-types = { workspace = true, features = ["codec"] }
dashcore_hashes = { path = "../hashes" }
hex = { version = "0.4" }
internals = { path = "../internals", package = "dashcore-private" }
Expand All @@ -36,3 +37,4 @@ unexpected_cfgs = { level = "deny", check-cfg = ['cfg(bench)', 'cfg(fuzzing)', '

[dev-dependencies]
hex_lit = { version = "0.1.1", features = ["rust_v_1_46"] }
serde_json = { version = "1.0" }
32 changes: 1 addition & 31 deletions crypto/src/bls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@

//! BLS12-381 public key and signatures.

use core::str::FromStr;

#[cfg(feature = "bls")]
use dash_pkc::__deps::ff::PrimeField;
#[cfg(feature = "bls")]
use dash_pkc::bls::{
BlsPublicKey as PkcPublicKey, BlsScChia, BlsScIetf, BlsScheme as PkcScheme,
BlsSecretKey as PkcSecretKey, BlsSignature as PkcSignature, Fr,
};
use dash_types::{make_bytes, make_sbytes, type_cvrt};
use dash_types::{make_bytes, make_sbytes};
use hex::FromHexError;
#[cfg(feature = "bls")]
use thiserror::Error as ThisError;
Expand Down Expand Up @@ -107,14 +105,6 @@ impl BlsPkBytes {
}
}

impl FromStr for BlsPkBytes {
type Err = FromHexError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_hex(s)
}
}

#[cfg(feature = "bincode")]
impl bincode::Encode for BlsPkBytes {
fn encode<E: bincode::enc::Encoder>(
Expand Down Expand Up @@ -143,12 +133,6 @@ impl<'de, C> bincode::BorrowDecode<'de, C> for BlsPkBytes {
}
}

type_cvrt!(
for[] TryFrom<&[u8]> for BlsPkBytes,
core::array::TryFromSliceError,
|v| Ok(Self::from_bytes(<[u8; BLS_PK_LEN]>::try_from(*v)?))
);

/// A [`BlsPkBytes`] paired with the scheme to read it under.
#[cfg(feature = "bls")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -360,14 +344,6 @@ impl BlsSigBytes {
}
}

impl FromStr for BlsSigBytes {
type Err = FromHexError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_hex(s)
}
}

#[cfg(feature = "bincode")]
impl bincode::Encode for BlsSigBytes {
fn encode<E: bincode::enc::Encoder>(
Expand Down Expand Up @@ -396,12 +372,6 @@ impl<'de, C> bincode::BorrowDecode<'de, C> for BlsSigBytes {
}
}

type_cvrt!(
for[] TryFrom<&[u8]> for BlsSigBytes,
core::array::TryFromSliceError,
|v| Ok(Self::from_bytes(<[u8; BLS_SIG_LEN]>::try_from(*v)?))
);

/// A [`BlsSigBytes`] paired with the scheme to read it under.
#[cfg(feature = "bls")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
Expand Down
188 changes: 188 additions & 0 deletions crypto/src/eddsa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
//
// This file is a part of rust-dashcore.
// SPDX-License-Identifier: CC0-1.0
// See the accompanying file LICENSE or https://creativecommons.org/publicdomain/zero/1.0
//

//! Ed25519 keys for Platform node identity.

#[cfg(feature = "eddsa")]
use dash_pkc::eddsa::{
EddsaPkBytes as PkcPkBytes, EddsaPublicKey as PkcPublicKey, EddsaSecretKey as PkcSecretKey,
};
#[cfg(feature = "eddsa")]
use dash_types::Hashable;
use dash_types::{make_bytes, make_sbytes};
#[cfg(feature = "eddsa")]
use thiserror::Error as ThisError;

/// Raw Ed25519 public key length.
pub const EDDSA_PK_LEN: usize = 32;

/// Ed25519 public key hash length.
pub const EDDSA_PK_HASH_LEN: usize = 20;

/// Raw Ed25519 secret key (seed) length.
pub const EDDSA_SK_LEN: usize = 32;

/// Errors produced by Ed25519 operations.
#[cfg(feature = "eddsa")]
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, ThisError)]
pub enum EddsaError {
/// Public key bytes are not a usable curve point.
#[error("Invalid Ed25519 public key: {0}")]
InvalidPublicKey(String),
}

make_bytes! {
/// Ed25519 public key hash (20 bytes).
EddsaPkHash, EDDSA_PK_HASH_LEN, rev
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

impl EddsaPkHash {
/// Wraps the canonical (i.e. Tenderdash) order bytes.
pub fn from_canonical_bytes(mut bytes: [u8; EDDSA_PK_HASH_LEN]) -> Self {
bytes.reverse();
Self::from_bytes(bytes)
}

/// Copies out the canonical (i.e. Tenderdash) order bytes.
pub fn to_canonical_bytes(self) -> [u8; EDDSA_PK_HASH_LEN] {
let mut bytes = self.to_bytes();
bytes.reverse();
bytes
}
}

#[cfg(feature = "bincode")]
impl bincode::Encode for EddsaPkHash {
fn encode<E: bincode::enc::Encoder>(
&self,
encoder: &mut E,
) -> Result<(), bincode::error::EncodeError> {
self.to_canonical_bytes().encode(encoder)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}

#[cfg(feature = "bincode")]
impl<C> bincode::Decode<C> for EddsaPkHash {
fn decode<D: bincode::de::Decoder<Context = C>>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
Ok(Self::from_canonical_bytes(<[u8; EDDSA_PK_HASH_LEN]>::decode(decoder)?))
}
}

#[cfg(feature = "bincode")]
impl<'de, C> bincode::BorrowDecode<'de, C> for EddsaPkHash {
fn borrow_decode<D: bincode::de::BorrowDecoder<'de, Context = C>>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
Ok(Self::from_canonical_bytes(<[u8; EDDSA_PK_HASH_LEN]>::borrow_decode(decoder)?))
}
}

make_bytes! {
/// Ed25519 public key (32 bytes, unvalidated).
EddsaPkBytes, EDDSA_PK_LEN
}

#[cfg(feature = "eddsa")]
impl EddsaPkBytes {
/// Checks these bytes are a usable curve point.
///
/// # Errors
///
/// Returns `InvalidPublicKey` when the bytes are not on the curve.
pub fn validate(&self) -> Result<(), EddsaError> {
PkcPublicKey::from_bytes(self.as_bytes())
.map(|_| ())
.map_err(|e| EddsaError::InvalidPublicKey(e.to_string()))
}

/// The CometBFT hash of the public key.
pub fn hash(&self) -> EddsaPkHash {
EddsaPkHash::from_bytes(
*Hashable::hash(&PkcPkBytes::from_bytes(*self.as_bytes())).as_bytes(),
)
}
}

make_sbytes! {
/// Ed25519 secret key seed (32 bytes).
EddsaSkBytes, EDDSA_SK_LEN
}

#[cfg(feature = "eddsa")]
impl EddsaSkBytes {
/// Derives the corresponding public key.
pub fn public_key(&self) -> EddsaPkBytes {
EddsaPkBytes::from_bytes(PkcSecretKey::from_bytes(self.as_bytes()).public_key().to_bytes())
}
}

#[cfg(test)]
mod tests {
use super::*;

/// Persisted masternode-list snapshots were written while the platform
/// node id was typed as `PubkeyHash`; the bincode layout must stay
/// identical so old snapshots keep decoding.
#[cfg(feature = "bincode")]
#[test]
fn bincode_layout_matches_pubkey_hash() {
use dashcore_hashes::Hash as _;

let bytes = [
0x8b, 0xaa, 0xdf, 0xf0, 0x0d, 0x8b, 0xaa, 0xdf, 0xf0, 0x0d, 0x8b, 0xaa, 0xdf, 0xf0,
0x0d, 0x8b, 0xaa, 0xdf, 0xf0, 0x0d,
];
let config = bincode::config::standard();
let hash_bytes = bincode::encode_to_vec(EddsaPkHash::from_canonical_bytes(bytes), config)
.expect("encode hash");
let pubkey_hash_bytes =
bincode::encode_to_vec(crate::key::PubkeyHash::from_byte_array(bytes), config)
.expect("encode pubkey hash");
assert_eq!(hash_bytes, pubkey_hash_bytes);
assert_eq!(hash_bytes, bytes, "the canonical order is the persisted order");

let (decoded, _): (EddsaPkHash, _) =
bincode::decode_from_slice(&hash_bytes, config).expect("decode hash");
assert_eq!(decoded.to_canonical_bytes(), bytes);
}

#[test]
fn hex_display_and_parse_round_trip() {
let hex = "4cd2ca50b36e0a2bb1b6b29da140448b47eeb7a1";
let hash: EddsaPkHash = hex.parse().expect("parse hash hex");
assert_eq!(hash.to_string(), hex);
assert_eq!(format!("{:?}", hash), format!("EddsaPkHash({})", hex));
assert!("abcd".parse::<EddsaPkHash>().is_err(), "wrong-length hex must fail");
assert!("zz".repeat(20).parse::<EddsaPkHash>().is_err(), "non-hex input must fail");
}

#[cfg(feature = "serde")]
#[test]
fn serde_json_round_trip_as_hex_string() {
let hash = EddsaPkHash::from_bytes([0x11; 20]);
let json = serde_json::to_string(&hash).expect("serialize hash");
assert_eq!(json, format!("\"{}\"", "11".repeat(20)));
let back: EddsaPkHash = serde_json::from_str(&json).expect("deserialize hash");
assert_eq!(back, hash);
}

#[cfg(feature = "eddsa")]
#[test]
fn hash_is_truncated_sha256() {
use dashcore_hashes::{sha256, Hash};

let public_key = [7u8; 32];
let digest = sha256::Hash::hash(&public_key);
// `EddsaPkHash` holds the wire order, which is the byte-reversal of
// the canonical form the digest is read in.
let mut canonical = *EddsaPkBytes::from_bytes(public_key).hash().as_bytes();
canonical.reverse();

assert_eq!(canonical[..], digest.to_byte_array()[..20]);
}
}
1 change: 1 addition & 0 deletions crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub(crate) mod serde_utils;

pub mod bls;
pub mod ecdsa;
pub mod eddsa;
pub mod key;
pub mod sighash;
pub mod taproot;
Expand Down
5 changes: 2 additions & 3 deletions dash/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ secp-recovery = ["secp256k1/recovery"]
signer = ["secp-recovery", "rand", "base64"]
core-block-hash-use-x11 = ["dashcore_hashes/x11"]
bls = ["dashcore-crypto/bls"]
eddsa = ["ed25519-dalek"]
eddsa = ["dashcore-crypto/eddsa"]
quorum_validation = ["bls"]
message_verification = ["bls"]
bincode = [ "dep:bincode", "dep:bincode_derive", "dashcore-crypto/bincode", "dashcore_hashes/bincode", "dash-network/bincode" ]
Expand All @@ -46,7 +46,7 @@ bech32 = { version = "0.9.1" }
dashcore_hashes = { path = "../hashes" }
dashcore-crypto = { path = "../crypto" }
dash-network = { path = "../dash-network" }
dash-types = { version = "=0.1.0-beta", default-features = false, features = ["codec"] }
dash-types = { workspace = true, features = ["codec"] }
secp256k1 = { version = "0.33.1" }
serde = { version = "1.0.219", default-features = false, features = [ "derive", "alloc", "rc" ], optional = true }

Expand All @@ -57,7 +57,6 @@ anyhow = { version= "1.0" }
hex = { version= "0.4" }
bincode = { workspace = true, optional = true }
bincode_derive = { workspace = true, optional = true }
ed25519-dalek = { version = "2.1", features = ["rand_core"], optional = true }
blake3 = "1.8.1"
thiserror = "2"
bitvec = "1.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,7 @@ mod tests {

let decoded: ProviderRegistrationPayload =
deserialize(&encoded).expect("deserialize zero-filled payload");
assert_eq!(decoded.platform_node_id, Some(crate::PlatformNodeId::from_byte_array([0; 20])));
assert_eq!(decoded.platform_node_id, Some(crate::PlatformNodeId::from_bytes([0; 20])));
assert_eq!(decoded.platform_p2p_port, Some(0));
assert_eq!(decoded.platform_http_port, Some(0));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ mod tests {
port: 0,
script_payout: ScriptBuf::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
inputs_hash: InputsHash::all_zeros(),
platform_node_id: Some(PlatformNodeId::from_byte_array([0; 20])),
platform_node_id: Some(PlatformNodeId::from_bytes([0; 20])),
platform_p2p_port: Some(0),
platform_http_port: Some(0),
payload_sig: BLSSignature::from([0; 96]),
Expand Down Expand Up @@ -448,7 +448,7 @@ mod tests {
assert_eq!(encoded.len(), 207);

let decoded = ProviderUpdateServicePayload::consensus_decode(&mut &encoded[..]).unwrap();
assert_eq!(decoded.platform_node_id, Some(PlatformNodeId::from_byte_array([0; 20])));
assert_eq!(decoded.platform_node_id, Some(PlatformNodeId::from_bytes([0; 20])));
assert_eq!(decoded.platform_p2p_port, Some(0));
assert_eq!(decoded.platform_http_port, Some(0));
}
Expand Down
Loading
Loading