Skip to content

Commit

Permalink
Merge pull request kaspanet#7 from biryukovmaxim/serde-bytes-as-hex-f…
Browse files Browse the repository at this point in the history
…or-bytes

Serde bytes as hex for bytes
  • Loading branch information
aspect authored Aug 11, 2023
2 parents 24aef94 + 915acdb commit 2e4d26a
Show file tree
Hide file tree
Showing 19 changed files with 774 additions and 123 deletions.
13 changes: 3 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion consensus/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ workflow-wasm.workspace = true
rand.workspace = true
workflow-log.workspace = true
workflow-core.workspace = true
serde_bytes.workspace = true

# secp256k1 = { version = "0.24", features = ["global-context", "rand-std", "serde"] }
# secp256k1 = { version = "0.24", features = ["global-context", "rand-std"] }
Expand Down
21 changes: 17 additions & 4 deletions consensus/core/src/subnets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,36 @@ use std::str::{self, FromStr};

use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use kaspa_utils::hex::{FromHex, ToHex};
use serde::{Deserialize, Serialize};
use kaspa_utils::{serde_impl_deser_fixed_bytes_ref, serde_impl_ser_fixed_bytes_ref};

/// The size of the array used to store subnetwork IDs.
pub const SUBNETWORK_ID_SIZE: usize = 20;

/// The domain representation of a Subnetwork ID
#[derive(
Debug, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize, BorshSerialize, BorshDeserialize, BorshSchema,
)]
#[derive(Debug, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Hash, BorshSerialize, BorshDeserialize, BorshSchema)]
pub struct SubnetworkId([u8; SUBNETWORK_ID_SIZE]);

serde_impl_ser_fixed_bytes_ref!(SubnetworkId, SUBNETWORK_ID_SIZE);
serde_impl_deser_fixed_bytes_ref!(SubnetworkId, SUBNETWORK_ID_SIZE);

impl AsRef<[u8; SUBNETWORK_ID_SIZE]> for SubnetworkId {
fn as_ref(&self) -> &[u8; SUBNETWORK_ID_SIZE] {
&self.0
}
}

impl AsRef<[u8]> for SubnetworkId {
fn as_ref(&self) -> &[u8] {
&self.0
}
}

impl From<[u8; SUBNETWORK_ID_SIZE]> for SubnetworkId {
fn from(value: [u8; SUBNETWORK_ID_SIZE]) -> Self {
Self::from_bytes(value)
}
}

impl SubnetworkId {
pub const fn from_byte(b: u8) -> SubnetworkId {
let mut bytes = [0u8; SUBNETWORK_ID_SIZE];
Expand Down
71 changes: 64 additions & 7 deletions consensus/core/src/tx.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use kaspa_utils::hex::*;
use kaspa_utils::{hex::*, serde_bytes, serde_bytes_fixed_ref};

use serde::{Deserialize, Deserializer, Serialize, Serializer};
use smallvec::SmallVec;
use std::{
Expand Down Expand Up @@ -31,6 +32,7 @@ pub type ScriptVec = SmallVec<[u8; SCRIPT_VECTOR_SIZE]>;
/// Represents the ScriptPublicKey Version
pub type ScriptPublicKeyVersion = u16;

use kaspa_utils::serde_bytes::FromHexVisitor;
/// Alias the `smallvec!` macro to ease maintenance
pub use smallvec::smallvec as scriptvec;

Expand All @@ -45,6 +47,14 @@ pub struct ScriptPublicKey {
script: ScriptVec, // Kept private to preserve read-only semantics
}

impl FromHex for ScriptPublicKey {
type Error = faster_hex::Error;

fn from_hex(hex_str: &str) -> Result<Self, Self::Error> {
ScriptPublicKey::from_str(hex_str)
}
}

#[derive(Default, Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Hash)]
#[serde(rename_all = "camelCase")]
#[serde(rename = "ScriptPublicKey")]
Expand Down Expand Up @@ -75,8 +85,7 @@ impl<'de: 'a, 'a> Deserialize<'de> for ScriptPublicKey {
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = <&str as Deserialize>::deserialize(deserializer)?;
FromStr::from_str(s).map_err(serde::de::Error::custom)
deserializer.deserialize_str(FromHexVisitor::default())
} else {
ScriptPublicKeyInternal::deserialize(deserializer)
.map(|ScriptPublicKeyInternal { script, version }| Self { version, script: SmallVec::from_slice(script) })
Expand Down Expand Up @@ -200,6 +209,7 @@ pub type TransactionIndexType = u32;
#[derive(Eq, Hash, PartialEq, Debug, Copy, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize, BorshSchema)]
#[serde(rename_all = "camelCase")]
pub struct TransactionOutpoint {
#[serde(with = "serde_bytes_fixed_ref")]
pub transaction_id: TransactionId,
pub index: TransactionIndexType,
}
Expand Down Expand Up @@ -262,6 +272,7 @@ pub struct Transaction {

// A field that is used to cache the transaction ID.
// Always use the corresponding self.id() instead of accessing this field directly
#[serde(with = "serde_bytes_fixed_ref")]
id: TransactionId,
}

Expand Down Expand Up @@ -520,7 +531,7 @@ pub type SignableTransaction = MutableTransaction<Transaction>;
#[cfg(test)]
mod tests {
use super::*;
use crate::subnets::SUBNETWORK_ID_COINBASE;
use consensus_core::subnets::SUBNETWORK_ID_COINBASE;
use smallvec::smallvec;

fn test_transaction() -> Transaction {
Expand Down Expand Up @@ -589,7 +600,7 @@ mod tests {
let bts = bincode::serialize(&tx).unwrap();

// standard, based on https://github.com/kaspanet/rusty-kaspa/commit/7e947a06d2434daf4bc7064d4cd87dc1984b56fe
let kos_bytes = vec![
let expected_bts = vec![
1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 22, 94, 56, 232, 179, 145, 69, 149, 217, 198, 65, 243, 184, 238, 194, 243, 70, 17, 137, 107,
130, 26, 104, 59, 122, 78, 222, 254, 44, 0, 0, 0, 250, 255, 255, 255, 32, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 2, 0, 0, 0, 0, 0, 0, 0, 3, 75,
Expand All @@ -607,15 +618,61 @@ mod tests {
64, 98, 49, 45, 0, 77, 32, 25, 122, 77, 15, 211, 252, 61, 210, 82, 177, 39, 153, 127, 33, 188, 172, 138, 38, 67, 75, 241,
176,
];
assert_eq!(kos_bytes, bts);
assert_eq!(expected_bts, bts);
assert_eq!(tx, bincode::deserialize(&bts).unwrap());
}

#[test]
fn test_transaction_json() {
let tx = test_transaction();
let str = serde_json::to_string_pretty(&tx).unwrap();
let expected_str = r#"{
"version": 1,
"inputs": [
{
"previousOutpoint": {
"transactionId": "165e38e8b3914595d9c641f3b8eec2f34611896b821a683b7a4edefe2c000000",
"index": 4294967290
},
"signatureScript": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
"sequence": 2,
"sigOpCount": 3
},
{
"previousOutpoint": {
"transactionId": "4bb07535dfd58e0b3cd64fd7155280872a0471bcf83095526ace0e38c6000000",
"index": 4294967291
},
"signatureScript": "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
"sequence": 4,
"sigOpCount": 5
}
],
"outputs": [
{
"value": 6,
"scriptPublicKey": "000076a921032f7e430aa4c9d159437e84b975dc76d9003bf0922cf3aa4528464bab780dba5e"
},
{
"value": 7,
"scriptPublicKey": "000076a921032f7e430aa4c9d159437e84b975dc76d9003bf0922cf3aa4528464bab780dba5e"
}
],
"lockTime": 8,
"subnetworkId": "0100000000000000000000000000000000000000",
"gas": 9,
"payload": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f60616263",
"id": "4592c14062312d004d20197a4d0fd3fc3dd252b127997f21bcac8a26434bf1b0"
}"#;
assert_eq!(expected_str, str);
assert_eq!(tx, serde_json::from_str(&str).unwrap());
}

#[test]
fn test_spk_serde_json() {
let vec = (0..SCRIPT_VECTOR_SIZE as u8).collect::<Vec<_>>();
let spk = ScriptPublicKey::from_vec(0xc0de, vec.clone());
let hex = serde_json::to_string(&spk).unwrap();
let hex: String = serde_json::to_string(&spk).unwrap();
assert_eq!("\"c0de000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223\"", hex);
let spk = serde_json::from_str::<ScriptPublicKey>(&hex).unwrap();
assert_eq!(spk.version, 0xc0de);
Expand Down
126 changes: 27 additions & 99 deletions crypto/hashes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ mod hashers;
mod pow_hashers;

use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use kaspa_utils::hex::{FromHex, ToHex};
use serde::{
de::Error as DeserializeError,
de::{SeqAccess, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
use kaspa_utils::{
hex::{FromHex, ToHex},
serde_impl_deser_fixed_bytes_ref, serde_impl_ser_fixed_bytes_ref,
};
use std::marker::PhantomData;
use std::{
array::TryFromSliceError,
fmt::{Debug, Display, Formatter},
Expand All @@ -27,6 +24,23 @@ pub use hashers::*;
#[wasm_bindgen]
pub struct Hash([u8; HASH_SIZE]);

serde_impl_ser_fixed_bytes_ref!(Hash, HASH_SIZE);
serde_impl_deser_fixed_bytes_ref!(Hash, HASH_SIZE);

impl From<[u8; HASH_SIZE]> for Hash {
fn from(value: [u8; HASH_SIZE]) -> Self {
Hash(value)
}
}

impl TryFrom<&[u8]> for Hash {
type Error = TryFromSliceError;

fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Hash::try_from_slice(value)
}
}

impl Hash {
#[inline(always)]
pub const fn from_bytes(bytes: [u8; HASH_SIZE]) -> Self {
Expand Down Expand Up @@ -126,6 +140,13 @@ impl From<u64> for Hash {
}
}

impl AsRef<[u8; HASH_SIZE]> for Hash {
#[inline(always)]
fn as_ref(&self) -> &[u8; HASH_SIZE] {
&self.0
}
}

impl AsRef<[u8]> for Hash {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
Expand All @@ -145,99 +166,6 @@ impl FromHex for Hash {
Self::from_str(hex_str)
}
}
impl Serialize for Hash {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
let mut hex = [0u8; HASH_SIZE * 2];
faster_hex::hex_encode(&self.0, &mut hex).expect("The output is exactly twice the size of the input");
serializer.serialize_str(str::from_utf8(&hex).expect("hex is always valid UTF-8"))
} else {
serializer.serialize_newtype_struct("Hash", &self.0)
}
}
}

struct HashVisitor<'de> {
marker: PhantomData<Hash>,
lifetime: PhantomData<&'de ()>,
}
impl<'de> Visitor<'de> for HashVisitor<'de> {
type Value = Hash;

fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("tuple struct Hash")
}
#[inline]
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: DeserializeError,
{
Hash::try_from_slice(v.as_bytes()).map_err(serde::de::Error::custom)
}
#[inline]
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: DeserializeError,
{
Hash::try_from_slice(v.as_bytes()).map_err(serde::de::Error::custom)
}
#[inline]
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: DeserializeError,
{
Hash::try_from_slice(v).map_err(serde::de::Error::custom)
}
#[inline]
fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
where
E: DeserializeError,
{
Hash::try_from_slice(v).map_err(serde::de::Error::custom)
}
#[inline]
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
where
E: DeserializeError,
{
Hash::try_from_slice(&v).map_err(serde::de::Error::custom)
}
#[inline]
fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
<[u8; HASH_SIZE] as serde::Deserialize>::deserialize(deserializer).map(Hash)
}
#[inline]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let Some(value) = seq.next_element()? else {
return Err(serde::de::Error::invalid_length(0usize, &"tuple struct Hash with 1 element"));
} ;
Ok(Hash(value))
}
}
// _serde::Deserializer::deserialize_newtype_struct(__deserializer, "Hash", __Visitor { marker: _serde::__private::PhantomData::<Hash>, lifetime: _serde::__private::PhantomData })

impl<'de> Deserialize<'de> for Hash {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = <std::string::String as Deserialize>::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(serde::de::Error::custom)
} else {
deserializer.deserialize_newtype_struct("Hash", HashVisitor { marker: Default::default(), lifetime: Default::default() })
}
}
}

#[wasm_bindgen]
impl Hash {
Expand Down
2 changes: 1 addition & 1 deletion protocol/p2p/src/convert/subnets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::error::ConversionError;

impl From<SubnetworkId> for protowire::SubnetworkId {
fn from(item: SubnetworkId) -> Self {
Self { bytes: item.as_ref().to_vec() }
Self { bytes: <SubnetworkId as AsRef<[u8]>>::as_ref(&item).to_vec() }
}
}

Expand Down
Loading

0 comments on commit 2e4d26a

Please sign in to comment.