From abe3d29e611d3879278b933d018209c6a760c4b7 Mon Sep 17 00:00:00 2001 From: 0xZensh Date: Sun, 7 Jan 2024 21:50:02 +0800 Subject: [PATCH] feat: improve state data struct (must rebuild inscription chain) --- crates/ns-fetcher/Cargo.toml | 4 +- crates/ns-fetcher/src/fetcher.rs | 1 + crates/ns-indexer/Cargo.toml | 4 +- crates/ns-indexer/src/api/name.rs | 6 +- crates/ns-indexer/src/db/model_inscription.rs | 38 +- crates/ns-indexer/src/db/model_name_state.rs | 25 +- crates/ns-indexer/src/envelope.rs | 14 +- crates/ns-indexer/src/indexer.rs | 37 +- crates/ns-indexer/src/utxo.rs | 2 +- crates/ns-inscriber/Cargo.toml | 6 +- crates/ns-inscriber/src/bin/main.rs | 6 +- crates/ns-inscriber/src/inscriber.rs | 14 +- crates/ns-protocol/Cargo.toml | 2 +- crates/ns-protocol/src/ns.rs | 455 +++++++++++------ crates/ns-protocol/src/state.rs | 465 ++++++++++++++++-- 15 files changed, 799 insertions(+), 280 deletions(-) diff --git a/crates/ns-fetcher/Cargo.toml b/crates/ns-fetcher/Cargo.toml index d318b81..a539bcc 100644 --- a/crates/ns-fetcher/Cargo.toml +++ b/crates/ns-fetcher/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ns-fetcher" -version = "0.1.0" +version = "0.2.0" edition = "2021" rust-version = "1.64" description = "Fetch and validate inscriptions from ns-indexer service" @@ -11,7 +11,7 @@ license = "CC0-1.0" [lib] [dependencies] -ns-protocol = { path = "../ns-protocol", version = "0.6" } +ns-protocol = { path = "../ns-protocol", version = "0.7" } anyhow = { workspace = true } bytes = { workspace = true } base64 = { workspace = true } diff --git a/crates/ns-fetcher/src/fetcher.rs b/crates/ns-fetcher/src/fetcher.rs index a3f1b7d..4ae104f 100644 --- a/crates/ns-fetcher/src/fetcher.rs +++ b/crates/ns-fetcher/src/fetcher.rs @@ -36,6 +36,7 @@ pub fn fetch_desc( head_height -= 1; let inscription: Inscription = cli.get_inscription_by_height(head_height).await?; + if head_inscription.previous_hash != inscription.hash()? { Err(anyhow::anyhow!("inscription({}): previous hash mismatch", inscription.height))?; } diff --git a/crates/ns-indexer/Cargo.toml b/crates/ns-indexer/Cargo.toml index 30fa74b..0426847 100644 --- a/crates/ns-indexer/Cargo.toml +++ b/crates/ns-indexer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ns-indexer" -version = "0.4.1" +version = "0.5.0" edition = "2021" rust-version = "1.64" description = "Name & Service Protocol indexer service in Rust" @@ -15,7 +15,7 @@ name = "ns-indexer" path = "src/bin/main.rs" [dependencies] -ns-protocol = { path = "../ns-protocol", version = "0.6" } +ns-protocol = { path = "../ns-protocol", version = "0.7" } ns-axum-web = { path = "../ns-axum-web", version = "0.1" } ns-scylla-orm = { path = "../ns-scylla-orm", version = "0.1" } ns-scylla-orm-macros = { path = "../ns-scylla-orm-macros", version = "0.1" } diff --git a/crates/ns-indexer/src/api/name.rs b/crates/ns-indexer/src/api/name.rs index 3a8b7c6..ab30fa9 100644 --- a/crates/ns-indexer/src/api/name.rs +++ b/crates/ns-indexer/src/api/name.rs @@ -10,7 +10,7 @@ use ns_axum_web::{ erring::{HTTPError, SuccessResponse}, object::PackObject, }; -use ns_protocol::state::NameState; +use ns_protocol::{ns, state::NameState}; use crate::api::{IndexerAPI, QueryName, QueryPubkey}; use crate::db; @@ -130,10 +130,12 @@ impl NameAPI { }; let pubkey = hex::decode(key) .map_err(|_| HTTPError::new(400, format!("Invalid pubkey: {}", input.pubkey)))?; + let pubkey = + ns::Bytes32::try_from(&pubkey).map_err(|e| HTTPError::new(400, e.to_string()))?; ctx.set_kvs(vec![("action", "list_names_by_pubkey".into())]) .await; - let mut names = db::NameState::list_by_pubkey(&app.scylla, pubkey.to_vec()).await?; + let mut names = db::NameState::list_by_pubkey(&app.scylla, pubkey).await?; names.sort(); Ok(to.with(SuccessResponse::new(names))) } diff --git a/crates/ns-indexer/src/db/model_inscription.rs b/crates/ns-indexer/src/db/model_inscription.rs index 2306e5a..b441b2d 100644 --- a/crates/ns-indexer/src/db/model_inscription.rs +++ b/crates/ns-indexer/src/db/model_inscription.rs @@ -2,7 +2,7 @@ use ns_axum_web::erring::HTTPError; use ns_scylla_orm::{ColumnsMap, CqlValue, ToCqlVal}; use ns_scylla_orm_macros::CqlOrm; -use ns_protocol::state; +use ns_protocol::{ns::Bytes32, state}; use crate::db::{self, scylladb, scylladb::filter_single_row_err}; @@ -150,13 +150,17 @@ impl Inscription { sequence: value.sequence as i64, height: value.height as i64, name_height: value.name_height as i64, - previous_hash: value.previous_hash.clone(), - name_hash: value.name_hash.clone(), - service_hash: value.service_hash.clone(), - protocol_hash: value.protocol_hash.as_ref().unwrap_or(&vec![]).clone(), - block_hash: value.block_hash.clone(), + previous_hash: (&value.previous_hash).into(), + name_hash: (&value.name_hash).into(), + service_hash: (&value.service_hash).into(), + protocol_hash: value + .protocol_hash + .as_ref() + .map(|v| v.into()) + .unwrap_or_default(), + block_hash: (&value.block_hash).into(), block_height: value.block_height as i64, - txid: value.txid.clone(), + txid: (&value.txid).into(), vin: value.vin as i8, data, _fields: Self::fields(), @@ -170,28 +174,28 @@ impl Inscription { sequence: self.sequence as u64, height: self.height as u64, name_height: self.name_height as u64, - previous_hash: self.previous_hash.clone(), - name_hash: self.name_hash.clone(), - service_hash: self.service_hash.clone(), + previous_hash: (&self.previous_hash).try_into()?, + name_hash: (&self.name_hash).try_into()?, + service_hash: (&self.service_hash).try_into()?, protocol_hash: if self.protocol_hash.is_empty() { None } else { - Some(self.protocol_hash.clone()) + Some((&self.protocol_hash).try_into()?) }, - block_hash: self.block_hash.clone(), + block_hash: (&self.block_hash).try_into()?, block_height: self.block_height as u64, - txid: self.txid.clone(), + txid: (&self.txid).try_into()?, vin: self.vin as u8, data, }) } - pub fn to_checkpoint(&self, hash: Vec) -> anyhow::Result { + pub fn to_checkpoint(&self, hash: Bytes32) -> anyhow::Result { Ok(Checkpoint { checkpoint: Checkpoint::LAST_ACCEPTED.to_string(), block_height: self.block_height, height: self.height, - hash, + hash: hash.into(), name: self.name.clone(), sequence: self.sequence, _fields: Checkpoint::fields(), @@ -510,7 +514,7 @@ impl InvalidInscription { Ok(Self { name: value.name.clone(), block_height: value.block_height as i64, - hash: value.hash.clone(), + hash: (&value.hash).into(), reason: value.reason.clone(), data, _fields: Self::fields(), @@ -522,7 +526,7 @@ impl InvalidInscription { Ok(state::InvalidInscription { name: self.name.clone(), block_height: self.block_height as u64, - hash: self.hash.clone(), + hash: (&self.hash).try_into()?, reason: self.reason.clone(), data, }) diff --git a/crates/ns-indexer/src/db/model_name_state.rs b/crates/ns-indexer/src/db/model_name_state.rs index 8129b8e..fb8efdf 100644 --- a/crates/ns-indexer/src/db/model_name_state.rs +++ b/crates/ns-indexer/src/db/model_name_state.rs @@ -3,7 +3,7 @@ use ns_scylla_orm::{ColumnsMap, CqlValue, ToCqlVal}; use ns_scylla_orm_macros::CqlOrm; use std::collections::{BTreeMap, HashSet}; -use ns_protocol::state; +use ns_protocol::{ns::Bytes32, state}; use crate::db::scylladb; @@ -57,8 +57,8 @@ impl NameState { expire_time: value.expire_time as i64, threshold: value.threshold as i8, key_kind: value.key_kind as i8, - public_keys: value.public_keys.clone(), - next_public_keys: value.next_public_keys.as_ref().unwrap_or(&vec![]).clone(), + public_keys: Bytes32::vec_into(&value.public_keys), + next_public_keys: Bytes32::vec_into(value.next_public_keys.as_ref().unwrap_or(&vec![])), _fields: Self::fields(), }) } @@ -73,11 +73,11 @@ impl NameState { expire_time: self.expire_time as u64, threshold: self.threshold as u8, key_kind: self.key_kind as u8, - public_keys: self.public_keys.clone(), + public_keys: Bytes32::vec_try_from(&self.public_keys)?, next_public_keys: if self.next_public_keys.is_empty() { None } else { - Some(self.next_public_keys.clone()) + Some(Bytes32::vec_try_from(&self.next_public_keys)?) }, }) } @@ -211,7 +211,7 @@ impl NameState { pub async fn batch_remove_pubkey_names( db: &scylladb::ScyllaDB, - pubkey_names: HashSet<(Vec, String)>, + pubkey_names: HashSet<(Bytes32, String)>, ) -> anyhow::Result<()> { let mut statements: Vec<&str> = Vec::with_capacity(pubkey_names.len()); let mut values: Vec<(Vec, String)> = Vec::with_capacity(pubkey_names.len()); @@ -220,7 +220,7 @@ impl NameState { let query = "DELETE FROM pubkey_name WHERE pubk=? AND name=?"; for state in pubkey_names { statements.push(query); - values.push((state.0, state.1)); + values.push((state.0.to_vec(), state.1)); } let _ = db .batch(scylladb::BatchType::Unlogged, statements, values) @@ -230,7 +230,7 @@ impl NameState { pub async fn batch_add_pubkey_names( db: &scylladb::ScyllaDB, - pubkey_names: HashSet<(Vec, String)>, + pubkey_names: HashSet<(Bytes32, String)>, ) -> anyhow::Result<()> { let mut statements: Vec<&str> = Vec::with_capacity(pubkey_names.len()); let mut values: Vec<(Vec, String)> = Vec::with_capacity(pubkey_names.len()); @@ -250,7 +250,7 @@ impl NameState { ); for state in pubkey_names { statements.push(query.as_str()); - values.push((state.0, state.1)); + values.push((state.0.to_vec(), state.1)); } let _ = db .batch(scylladb::BatchType::Unlogged, statements, values) @@ -283,15 +283,12 @@ impl NameState { pub async fn list_by_pubkey( db: &scylladb::ScyllaDB, - pubkey: Vec, + pubkey: Bytes32, ) -> anyhow::Result> { let fields = vec!["name".to_string()]; - if pubkey.len() != 32 { - return Ok(vec![]); - } let query = "SELECT name FROM pubkey_name WHERE pubkey=?"; - let params = (pubkey,); + let params = (pubkey.to_vec(),); let rows = db.execute_iter(query, params).await?; let mut res: Vec = Vec::with_capacity(rows.len()); diff --git a/crates/ns-indexer/src/envelope.rs b/crates/ns-indexer/src/envelope.rs index 0d45a7a..d58aad2 100644 --- a/crates/ns-indexer/src/envelope.rs +++ b/crates/ns-indexer/src/envelope.rs @@ -80,19 +80,23 @@ mod tests { use hex_literal::hex; use ns_protocol::{ ed25519, - ns::{Operation, PublicKeyParams, Service, ThresholdLevel}, + ns::{Bytes32, Operation, PublicKeyParams, Service, ThresholdLevel}, }; #[test] fn names_from_witness() { - let secret_key = hex!("7ef3811aabb916dc2f646ef1a371b90adec91bc07992cd4d44c156c42fc1b300"); - let public_key = hex!("ee90735ac719e85dc2f3e5974036387fdf478af7d9d1f8480e97eee601890266"); + let secret_key = Bytes32(hex!( + "7ef3811aabb916dc2f646ef1a371b90adec91bc07992cd4d44c156c42fc1b300" + )); + let public_key = Bytes32(hex!( + "ee90735ac719e85dc2f3e5974036387fdf478af7d9d1f8480e97eee601890266" + )); let params = PublicKeyParams { - public_keys: vec![public_key.to_vec()], + public_keys: vec![public_key], threshold: Some(1), kind: None, }; - let signer = ed25519::SigningKey::try_from(&secret_key).unwrap(); + let signer = ed25519::SigningKey::from_bytes(&secret_key.0); let signers = vec![signer]; let mut name1 = Name { diff --git a/crates/ns-indexer/src/indexer.rs b/crates/ns-indexer/src/indexer.rs index fd794b3..85d709e 100644 --- a/crates/ns-indexer/src/indexer.rs +++ b/crates/ns-indexer/src/indexer.rs @@ -11,7 +11,7 @@ use tokio::{ }; use ns_protocol::{ - ns::{Name, PublicKeyParams, ThresholdLevel}, + ns::{self, Bytes32, Name, PublicKeyParams, ThresholdLevel}, state::{ hash_sha3, Inscription, InvalidInscription, NameState, ServiceProtocol, ServiceState, NAME_EXPIRE_SECONDS, NAME_STALE_SECONDS, @@ -201,15 +201,15 @@ impl Indexer { let mut inscription = Inscription { name: name.name.clone(), sequence: name.sequence, - height: 0, - name_height: 0, - previous_hash: vec![], + height: 1, + name_height: 1, + previous_hash: Bytes32::default(), name_hash: name_state_hash, service_hash: service_state_hash, protocol_hash: service_protocol_hash, - block_hash: block_hash.to_byte_array().to_vec(), + block_hash: block_hash.to_byte_array().into(), block_height, - txid: envelope.txid.to_byte_array().to_vec(), + txid: envelope.txid.to_byte_array().into(), vin: envelope.vin, data: name, }; @@ -242,10 +242,7 @@ impl Indexer { .expect("hash_sha3(inscription) should not fail"); } None => { - // this is the first inscription - inscription.height = 1; - inscription.name_height = 1; - inscription.previous_hash = [0u8; 32].to_vec(); + // this is the first inscription, no thing to do } }, } @@ -272,7 +269,7 @@ impl Indexer { block_height: u64, block_time: u64, name: &Name, - ) -> anyhow::Result<(Vec, Vec, Option>)> { + ) -> anyhow::Result<(Bytes32, Bytes32, Option)> { name.validate()?; // default protocol is Name service let mut service_protocol = ServiceProtocol::default(); @@ -491,11 +488,11 @@ impl Indexer { } let mut fresh_name_index: BTreeMap = BTreeMap::new(); - let mut fresh_name_with_public_keys: HashSet<(String, Vec>)> = HashSet::new(); - let mut captured_name_with_public_keys: HashSet<(String, Vec>)> = HashSet::new(); - let mut stale_name_with_public_keys: HashSet<(String, Vec>)> = HashSet::new(); - let mut fresh_pubkey_names: HashSet<(Vec, String)> = HashSet::new(); - let mut stale_pubkey_names: HashSet<(Vec, String)> = HashSet::new(); + let mut fresh_name_with_public_keys: HashSet<(String, Vec)> = HashSet::new(); + let mut captured_name_with_public_keys: HashSet<(String, Vec)> = HashSet::new(); + let mut stale_name_with_public_keys: HashSet<(String, Vec)> = HashSet::new(); + let mut fresh_pubkey_names: HashSet<(Bytes32, String)> = HashSet::new(); + let mut stale_pubkey_names: HashSet<(Bytes32, String)> = HashSet::new(); if !inscriptions.is_empty() { for name in &name_states { fresh_name_index.insert(name.name.clone(), name.block_time); @@ -508,7 +505,13 @@ impl Indexer { ) .await?; for npk in npks { - captured_name_with_public_keys.insert((npk.name.clone(), npk.public_keys.clone())); + captured_name_with_public_keys.insert(( + npk.name.clone(), + npk.public_keys + .iter() + .map(Bytes32::try_from) + .collect::, ns::Error>>()?, + )); } for npk in &captured_name_with_public_keys { if fresh_name_with_public_keys.contains(npk) { diff --git a/crates/ns-indexer/src/utxo.rs b/crates/ns-indexer/src/utxo.rs index a1c514b..8df8204 100644 --- a/crates/ns-indexer/src/utxo.rs +++ b/crates/ns-indexer/src/utxo.rs @@ -17,7 +17,7 @@ impl UTXO { .input .iter() .map(|txin| UTXO { - txid: txin.previous_output.txid.to_byte_array().to_vec(), + txid: txin.previous_output.txid.to_byte_array().into(), vout: txin.previous_output.vout, amount: 0, }) diff --git a/crates/ns-inscriber/Cargo.toml b/crates/ns-inscriber/Cargo.toml index 7af3298..f88a1db 100644 --- a/crates/ns-inscriber/Cargo.toml +++ b/crates/ns-inscriber/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ns-inscriber" -version = "0.3.0" +version = "0.4.0" edition = "2021" rust-version = "1.64" description = "Name & Service Protocol inscriber service in Rust" @@ -15,8 +15,8 @@ name = "ns-inscriber" path = "src/bin/main.rs" [dependencies] -ns-protocol = { path = "../ns-protocol", version = "0.6" } -ns-indexer = { path = "../ns-indexer", version = "0.4" } +ns-protocol = { path = "../ns-protocol", version = "0.7" } +ns-indexer = { path = "../ns-indexer", version = "0.5" } anyhow = { workspace = true } bytes = { workspace = true } base64 = { workspace = true } diff --git a/crates/ns-inscriber/src/bin/main.rs b/crates/ns-inscriber/src/bin/main.rs index fcba898..104fdc0 100644 --- a/crates/ns-inscriber/src/bin/main.rs +++ b/crates/ns-inscriber/src/bin/main.rs @@ -19,7 +19,7 @@ use ns_inscriber::{ secp256k1, unwrap_cbor_tag, wrap_cbor_tag, DerivationPath, Encrypt0, Key, }, }; -use ns_protocol::ns::{Name, Operation, PublicKeyParams, Service, ThresholdLevel, Value}; +use ns_protocol::ns::{Bytes32, Name, Operation, PublicKeyParams, Service, ThresholdLevel, Value}; const AAD: &[u8; 12] = b"ns-inscriber"; const TRANSFER_KEY_AAD: &[u8; 20] = b"ns:transfer.cose.key"; @@ -506,7 +506,7 @@ async fn main() -> anyhow::Result<()> { } let signing_key = ed25519::SigningKey::from_bytes(&ed25519_key.secret_key()?); let params = PublicKeyParams { - public_keys: vec![signing_key.verifying_key().to_bytes().to_vec()], + public_keys: vec![Bytes32(signing_key.verifying_key().to_bytes().to_owned())], threshold: None, kind: None, }; @@ -583,7 +583,7 @@ async fn main() -> anyhow::Result<()> { } let signing_key = ed25519::SigningKey::from_bytes(&ed25519_key.secret_key()?); let params = PublicKeyParams { - public_keys: vec![signing_key.verifying_key().to_bytes().to_vec()], + public_keys: vec![Bytes32(signing_key.verifying_key().to_bytes().to_owned())], threshold: None, kind: None, }; diff --git a/crates/ns-inscriber/src/inscriber.rs b/crates/ns-inscriber/src/inscriber.rs index c4e40be..f359257 100644 --- a/crates/ns-inscriber/src/inscriber.rs +++ b/crates/ns-inscriber/src/inscriber.rs @@ -710,19 +710,23 @@ mod tests { use ns_indexer::envelope::Envelope; use ns_protocol::{ ed25519, - ns::{Operation, PublicKeyParams, Service, ThresholdLevel, Value}, + ns::{Bytes32, Operation, PublicKeyParams, Service, ThresholdLevel, Value}, }; fn get_name(name: &str) -> Name { - let secret_key = hex!("7ef3811aabb916dc2f646ef1a371b90adec91bc07992cd4d44c156c42fc1b300"); - let public_key = hex!("ee90735ac719e85dc2f3e5974036387fdf478af7d9d1f8480e97eee601890266"); + let secret_key = Bytes32(hex!( + "7ef3811aabb916dc2f646ef1a371b90adec91bc07992cd4d44c156c42fc1b300" + )); + let public_key = Bytes32(hex!( + "ee90735ac719e85dc2f3e5974036387fdf478af7d9d1f8480e97eee601890266" + )); let params = PublicKeyParams { - public_keys: vec![public_key.to_vec()], + public_keys: vec![public_key], threshold: Some(1), kind: None, }; - let signer = ed25519::SigningKey::try_from(&secret_key).unwrap(); + let signer = ed25519::SigningKey::from_bytes(&secret_key.0); let signers = vec![signer]; let mut name = Name { diff --git a/crates/ns-protocol/Cargo.toml b/crates/ns-protocol/Cargo.toml index 945c28c..e0ba7d0 100644 --- a/crates/ns-protocol/Cargo.toml +++ b/crates/ns-protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ns-protocol" -version = "0.6.0" +version = "0.7.0" edition = "2021" rust-version = "1.64" description = "Name & Service Protocol in Rust" diff --git a/crates/ns-protocol/src/ns.rs b/crates/ns-protocol/src/ns.rs index 0b061b5..d7fe330 100644 --- a/crates/ns-protocol/src/ns.rs +++ b/crates/ns-protocol/src/ns.rs @@ -20,7 +20,7 @@ pub struct Name { pub name: String, pub sequence: u64, pub service: Service, - pub signatures: Vec, + pub signatures: Vec, } #[derive(Clone, PartialEq, Debug, Default)] @@ -45,15 +45,232 @@ impl core::default::Default for Operation { } } -#[derive(Clone, PartialEq, Eq, Debug, Default)] -pub struct Signature(pub Vec); +#[derive(Clone, PartialEq, Debug)] +pub(crate) struct IntValue<'a>(pub &'a Value); +impl<'a> IntValue<'a> { + fn to_int(&self) -> Result { + self.0.as_integer().ok_or_else(|| { + Error::Custom(format!( + "IntValue: expected integer, got {}", + kind_of_value(self.0) + )) + }) + } +} + +impl TryFrom<&IntValue<'_>> for u64 { + type Error = Error; + + fn try_from(value: &IntValue) -> Result { + u64::try_from(value.to_int()?) + .map_err(|err| Error::Custom(format!("IntValue: expected u64, error: {:?}", err))) + } +} + +impl TryFrom<&IntValue<'_>> for u32 { + type Error = Error; + + fn try_from(value: &IntValue) -> Result { + u32::try_from(value.to_int()?) + .map_err(|err| Error::Custom(format!("IntValue: expected u64, error: {:?}", err))) + } +} + +impl TryFrom<&IntValue<'_>> for u16 { + type Error = Error; + + fn try_from(value: &IntValue) -> Result { + u16::try_from(value.to_int()?) + .map_err(|err| Error::Custom(format!("IntValue: expected u64, error: {:?}", err))) + } +} + +impl TryFrom<&IntValue<'_>> for u8 { + type Error = Error; + + fn try_from(value: &IntValue) -> Result { + u8::try_from(value.to_int()?) + .map_err(|err| Error::Custom(format!("IntValue: expected u64, error: {:?}", err))) + } +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash, Default)] +pub struct Bytes32(pub [u8; 32]); + +impl From<[u8; 32]> for Bytes32 { + fn from(value: [u8; 32]) -> Self { + Bytes32(value) + } +} + +impl From<&[u8; 32]> for Bytes32 { + fn from(value: &[u8; 32]) -> Self { + Bytes32(value.to_owned()) + } +} + +impl From<&Bytes32> for Vec { + fn from(value: &Bytes32) -> Self { + value.to_vec() + } +} + +impl From for Vec { + fn from(value: Bytes32) -> Self { + value.to_vec() + } +} + +impl TryFrom<&[u8]> for Bytes32 { + type Error = Error; + + fn try_from(value: &[u8]) -> Result { + if value.len() != 32 { + Err(Error::Custom(format!( + "Bytes32: expected value length is 32, got {:?}", + value.len() + ))) + } else { + let mut bytes = [0u8; 32]; + bytes.copy_from_slice(value); + Ok(Bytes32(bytes)) + } + } +} + +impl TryFrom<&Vec> for Bytes32 { + type Error = Error; + fn try_from(value: &Vec) -> Result { + Bytes32::try_from(value.as_slice()) + } +} + +impl TryFrom> for Bytes32 { + type Error = Error; + fn try_from(value: Vec) -> Result { + Bytes32::try_from(value.as_slice()) + } +} + +impl Bytes32 { + pub fn to_vec(&self) -> Vec { + self.0.to_vec() + } + + pub fn vec_into(values: &[Self]) -> Vec> { + values.iter().map(|v| v.to_vec()).collect() + } + + pub fn vec_try_from(values: &[Vec]) -> Result, Error> { + values.iter().map(Bytes32::try_from).collect() + } + + pub fn vec_try_from_value(value: &Value) -> Result, Error> { + value + .as_array() + .ok_or_else(|| { + Error::Custom(format!( + "Bytes32: expected array, got {}", + kind_of_value(value) + )) + })? + .iter() + .map(Bytes32::try_from) + .collect::, Error>>() + } +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct Bytes64(pub [u8; 64]); + +impl core::default::Default for Bytes64 { + fn default() -> Self { + Bytes64([0u8; 64]) + } +} + +impl From<[u8; 64]> for Bytes64 { + fn from(value: [u8; 64]) -> Self { + Bytes64(value) + } +} + +impl From<&[u8; 64]> for Bytes64 { + fn from(value: &[u8; 64]) -> Self { + Bytes64(value.to_owned()) + } +} + +impl From<&Bytes64> for Vec { + fn from(value: &Bytes64) -> Self { + value.to_vec() + } +} + +impl From for Vec { + fn from(value: Bytes64) -> Self { + value.to_vec() + } +} + +impl TryFrom<&[u8]> for Bytes64 { + type Error = Error; + + fn try_from(value: &[u8]) -> Result { + if value.len() != 64 { + Err(Error::Custom(format!( + "Bytes64: expected value length is 64, got {:?}", + value.len() + ))) + } else { + let mut bytes = [0u8; 64]; + bytes.copy_from_slice(value); + Ok(Bytes64(bytes)) + } + } +} + +impl TryFrom<&Vec> for Bytes64 { + type Error = Error; + fn try_from(value: &Vec) -> Result { + Bytes64::try_from(value.as_slice()) + } +} + +impl Bytes64 { + pub fn to_vec(&self) -> Vec { + self.0.to_vec() + } + + pub fn vec_into(values: &[Self]) -> Vec> { + values.iter().map(|v| v.to_vec()).collect() + } + + pub fn vec_try_from(values: &[Vec]) -> Result, Error> { + values.iter().map(Bytes64::try_from).collect() + } + + pub fn vec_try_from_value(value: &Value) -> Result, Error> { + value + .as_array() + .ok_or_else(|| { + Error::Custom(format!( + "Bytes64: expected array, got {}", + kind_of_value(value) + )) + })? + .iter() + .map(Bytes64::try_from) + .collect::, Error>>() + } +} // PublicKeyParams is Ed25519 Multisignatures with threshold, // every public key can be FROST (Flexible Round-Optimised Schnorr Threshold signatures) // see: https://github.com/ZcashFoundation/frost #[derive(Clone, PartialEq, Eq, Debug, Default)] pub struct PublicKeyParams { - pub public_keys: Vec>, + pub public_keys: Vec, pub threshold: Option, // default to public_keys.len() pub kind: Option, // default to 0: ed25519 } @@ -74,14 +291,7 @@ impl PublicKeyParams { "PublicKeyParams: expected at least one public key".to_string(), )); } - for pk in self.public_keys.iter() { - if pk.len() != 32 { - return Err(Error::Custom(format!( - "PublicKeyParams: expected key length is 32, got {:?}", - pk.len() - ))); - } - } + if let Some(threshold) = self.threshold { if threshold == 0 { return Err(Error::Custom( @@ -249,14 +459,6 @@ impl Name { if self.signatures.is_empty() { return Err(Error::Custom("Name: missing signatures".to_string())); } - for sig in self.signatures.iter() { - if sig.0.len() != 64 { - return Err(Error::Custom(format!( - "Name: expected signature length is 64, got {:?}", - sig.0.len() - ))); - } - } let mut signatures = self.signatures.clone(); signatures.dedup(); @@ -278,15 +480,14 @@ impl Name { let data = self.to_sign_bytes()?; let mut keys: Vec = Vec::with_capacity(params.public_keys.len()); for pk in ¶ms.public_keys { - let key = ed25519::VerifyingKey::try_from(pk.as_slice()) + let key = ed25519::VerifyingKey::from_bytes(&pk.0) .map_err(|err| Error::Custom(err.to_string()))?; keys.push(key); } let mut count = 0; for sig in self.signatures.iter() { - let sig = ed25519::Signature::from_slice(&sig.0) - .map_err(|err| Error::Custom(err.to_string()))?; + let sig = ed25519::Signature::from_bytes(&sig.0); for key in keys.iter() { if key.verify_strict(&data, &sig).is_ok() { count += 1; @@ -324,9 +525,9 @@ impl Name { for pk in params.public_keys.iter() { if let Some(signer) = signers .iter() - .find(|sk| sk.verifying_key().as_bytes().as_slice() == pk) + .find(|sk| sk.verifying_key().as_bytes() == &pk.0) { - let sig = Signature(signer.sign(&data).to_bytes().to_vec()); + let sig = Bytes64::from(signer.sign(&data).to_bytes()); self.signatures.push(sig); if self.signatures.len() == threshold as usize { break; @@ -347,26 +548,28 @@ impl Name { pub fn sign_with(&mut self, signer: &ed25519::SigningKey) -> Result<(), Error> { let data = self.to_sign_bytes()?; - let sig = Signature(signer.sign(&data).to_bytes().to_vec()); + let sig = Bytes64::from(signer.sign(&data).to_bytes()); self.signatures.push(sig); Ok(()) } } -impl From<&Signature> for Value { - fn from(signature: &Signature) -> Self { - Value::Bytes(signature.0.clone()) +impl From<&Bytes32> for Value { + fn from(val: &Bytes32) -> Self { + Value::Bytes(val.to_vec()) + } +} + +impl From<&Bytes64> for Value { + fn from(val: &Bytes64) -> Self { + Value::Bytes(val.to_vec()) } } impl From<&PublicKeyParams> for Value { fn from(params: &PublicKeyParams) -> Self { let mut arr = vec![Value::Array( - params - .public_keys - .iter() - .map(|pk| Value::Bytes(pk.clone())) - .collect(), + params.public_keys.iter().map(Value::from).collect(), )]; if let Some(threshold) = params.threshold { arr.push(Value::Integer(threshold.into())); @@ -411,25 +614,28 @@ impl From<&Name> for Value { } } -impl TryFrom<&Value> for Signature { +impl TryFrom<&Value> for Bytes32 { type Error = Error; fn try_from(value: &Value) -> Result { match value { - Value::Bytes(bytes) => { - if bytes.len() != 64 { - Err(Error::Custom(format!( - "Signature: expected value length is 64, got {:?}", - bytes.len() - ))) - } else { - let mut value = Vec::with_capacity(64); - value.extend(bytes); - Ok(Signature(value)) - } - } + Value::Bytes(bytes) => Bytes32::try_from(bytes), _ => Err(Error::Custom(format!( - "Signature: expected bytes, got {}", + "Bytes32: expected bytes, got {}", + kind_of_value(value) + ))), + } + } +} + +impl TryFrom<&Value> for Bytes64 { + type Error = Error; + + fn try_from(value: &Value) -> Result { + match value { + Value::Bytes(bytes) => Bytes64::try_from(bytes), + _ => Err(Error::Custom(format!( + "Bytes64: expected bytes, got {}", kind_of_value(value) ))), } @@ -449,18 +655,7 @@ impl TryFrom<&Value> for Operation { } Ok(Operation { - subcode: arr[0] - .as_integer() - .ok_or_else(|| { - Error::Custom(format!( - "Operation: expected integer, got {}", - kind_of_value(&arr[0]) - )) - })? - .try_into() - .map_err(|err| { - Error::Custom(format!("Operation: expected u32, error, {:?}", err)) - })?, + subcode: u16::try_from(&IntValue(&arr[0]))?, params: arr[1].clone(), }) } @@ -484,18 +679,7 @@ impl TryFrom<&Value> for Service { match arr.len() { v if v == 2 || v == 3 => { let mut srv = Service { - code: arr[0] - .as_integer() - .ok_or_else(|| { - Error::Custom(format!( - "Service: expected integer, got {}", - kind_of_value(&arr[0]) - )) - })? - .try_into() - .map_err(|err| { - Error::Custom(format!("Service: expected u32, error: {:?}", err)) - })?, + code: u64::try_from(&IntValue(&arr[0]))?, operations: arr[1] .as_array() .ok_or_else(|| { @@ -540,56 +724,17 @@ impl TryFrom<&Value> for PublicKeyParams { match arr.len() { v if (1..=3).contains(&v) => { let mut params = PublicKeyParams { - public_keys: arr[0] - .as_array() - .ok_or_else(|| { - Error::Custom(format!( - "PublicKeyParams: expected array, got {}", - kind_of_value(&arr[0]) - )) - })? - .iter() - .map(|pk| { - pk.as_bytes().map(|v| v.to_owned()).ok_or_else(|| { - Error::Custom(format!( - "PublicKeyParams: expected bytes, got {}", - kind_of_value(pk) - )) - }) - }) - .collect::>, Error>>()?, + public_keys: Bytes32::vec_try_from_value(&arr[0])?, threshold: None, kind: None, }; if v >= 2 { - let threshold = arr[1] - .as_integer() - .ok_or_else(|| { - Error::Custom(format!( - "PublicKeyParams: expected integer, got {}", - kind_of_value(&arr[1]) - )) - })? - .try_into() - .map_err(|err| { - Error::Custom(format!("PublicKeyParams: expected u8, error: {:?}", err)) - })?; + let threshold = u8::try_from(&IntValue(&arr[1]))?; params.threshold = Some(threshold); } if v == 3 { - let kind = arr[2] - .as_integer() - .ok_or_else(|| { - Error::Custom(format!( - "PublicKeyParams: expected integer, got {}", - kind_of_value(&arr[2]) - )) - })? - .try_into() - .map_err(|err| { - Error::Custom(format!("PublicKeyParams: expected u8, error: {:?}", err)) - })?; + let kind = u8::try_from(&IntValue(&arr[2]))?; params.kind = Some(kind); } Ok(params) @@ -622,30 +767,9 @@ impl TryFrom<&Value> for Name { )) })? .to_string(), - sequence: arr[1] - .as_integer() - .ok_or_else(|| { - Error::Custom(format!( - "Name: expected integer, got {}", - kind_of_value(&arr[1]) - )) - })? - .try_into() - .map_err(|err| { - Error::Custom(format!("Name: expected u64, error: {:?}", err)) - })?, + sequence: u64::try_from(&IntValue(&arr[1]))?, service: Service::try_from(&arr[2])?, - signatures: arr[3] - .as_array() - .ok_or_else(|| { - Error::Custom(format!( - "Name: expected array, got {}", - kind_of_value(&arr[3]) - )) - })? - .iter() - .map(Signature::try_from) - .collect::, Self::Error>>()?, + signatures: Bytes64::vec_try_from_value(&arr[3])?, }), _ => Err(Error::Custom(format!( "Name: expected array of length 4, got {}", @@ -655,7 +779,26 @@ impl TryFrom<&Value> for Name { } } -impl Serialize for Signature { +impl Serialize for Bytes32 { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_bytes(&self.0) + } +} + +impl<'de> Deserialize<'de> for Bytes32 { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + let val = Value::deserialize(deserializer)?; + Bytes32::try_from(&val).map_err(de::Error::custom) + } +} + +impl Serialize for Bytes64 { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, @@ -664,13 +807,13 @@ impl Serialize for Signature { } } -impl<'de> Deserialize<'de> for Signature { - fn deserialize(deserializer: D) -> Result +impl<'de> Deserialize<'de> for Bytes64 { + fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { let val = Value::deserialize(deserializer)?; - Signature::try_from(&val).map_err(de::Error::custom) + Bytes64::try_from(&val).map_err(de::Error::custom) } } @@ -731,7 +874,7 @@ impl<'de> Deserialize<'de> for Name { } } -fn kind_of_value(v: &Value) -> String { +pub(crate) fn kind_of_value(v: &Value) -> String { match v { Value::Integer(_) => "integer".to_string(), Value::Bytes(_) => "bytes".to_string(), @@ -814,26 +957,30 @@ mod tests { #[test] fn signature_ser_de() { - let sig = Signature(hex!("6b71fd0c8ae2ccc910c39dd20e76653fccca2638b7935f2312e954f5dccd71b209c58ca57e9d4fc2d3c06a57d585dbadf4535abb8a9cf103eeb9b9717d87f201").to_vec()); + let sig = Bytes64(hex!("6b71fd0c8ae2ccc910c39dd20e76653fccca2638b7935f2312e954f5dccd71b209c58ca57e9d4fc2d3c06a57d585dbadf4535abb8a9cf103eeb9b9717d87f201")); let mut buf: Vec = Vec::new(); into_writer(&sig, &mut buf).unwrap(); assert_eq!(buf.len(), 66); assert_eq!(buf[0], 0x58); // byte string assert_eq!(buf[1], 0x40); // 64 bytes - let sig2: Signature = from_reader(&buf[..]).unwrap(); + let sig2: Bytes64 = from_reader(&buf[..]).unwrap(); assert_eq!(sig, sig2); } #[test] fn name_ser_de() { - let secret_key = hex!("7ef3811aabb916dc2f646ef1a371b90adec91bc07992cd4d44c156c42fc1b300"); - let public_key = hex!("ee90735ac719e85dc2f3e5974036387fdf478af7d9d1f8480e97eee601890266"); + let secret_key = Bytes32(hex!( + "7ef3811aabb916dc2f646ef1a371b90adec91bc07992cd4d44c156c42fc1b300" + )); + let public_key = Bytes32(hex!( + "ee90735ac719e85dc2f3e5974036387fdf478af7d9d1f8480e97eee601890266" + )); let params = PublicKeyParams { - public_keys: vec![public_key.to_vec()], + public_keys: vec![public_key], threshold: Some(1), kind: None, }; - let signer = ed25519::SigningKey::try_from(&secret_key).unwrap(); + let signer = ed25519::SigningKey::from_bytes(&secret_key.0); assert!(params.validate().is_ok()); let mut name = Name { @@ -857,8 +1004,8 @@ mod tests { assert!(name.verify(¶ms, ThresholdLevel::Default).is_ok()); assert!(name.verify(¶ms, ThresholdLevel::Strict).is_ok()); assert!(name.verify(¶ms, ThresholdLevel::All).is_ok()); - assert_eq!(name.signatures, vec![Signature( - hex!("e23554d996647e86f69115d04515398cc7463062d2683b099371360e93fa1cba02351492b70ef31037baa7780053bcf20b12bafe9531ee17fe140b93082a3f0c").to_vec(), + assert_eq!(name.signatures, vec![Bytes64( + hex!("e23554d996647e86f69115d04515398cc7463062d2683b099371360e93fa1cba02351492b70ef31037baa7780053bcf20b12bafe9531ee17fe140b93082a3f0c") )]); let data = name.to_bytes().unwrap(); @@ -889,10 +1036,10 @@ mod tests { let params = PublicKeyParams { public_keys: vec![ - s1.verifying_key().as_bytes().to_vec(), - s2.verifying_key().as_bytes().to_vec(), - s3.verifying_key().as_bytes().to_vec(), - s4.verifying_key().as_bytes().to_vec(), + s1.verifying_key().as_bytes().into(), + s2.verifying_key().as_bytes().into(), + s3.verifying_key().as_bytes().into(), + s4.verifying_key().as_bytes().into(), ], threshold: Some(2), kind: None, diff --git a/crates/ns-protocol/src/state.rs b/crates/ns-protocol/src/state.rs index 3c3cbd7..7e62674 100644 --- a/crates/ns-protocol/src/state.rs +++ b/crates/ns-protocol/src/state.rs @@ -1,16 +1,18 @@ -use ciborium::{from_reader, into_writer}; -use serde::{de, Deserialize, Serialize}; +use ciborium::{from_reader, into_writer, Value}; +use serde::{de, ser, Deserialize, Serialize}; use sha3::{Digest, Sha3_256}; use std::{borrow::BorrowMut, fmt::Debug}; -use crate::ns::{Error, Name, PublicKeyParams, Service, ThresholdLevel, Value}; +use crate::ns::{ + kind_of_value, Bytes32, Error, IntValue, Name, PublicKeyParams, Service, ThresholdLevel, +}; // After the silence period exceeds 365 days, the name is invalid, the application validation signature should be invalid, and the original registrant can activate the name with any update. pub const NAME_STALE_SECONDS: u64 = 60 * 60 * 24 * 365; // After the silence period exceeds 365 + 180 days, others are allowed to re-register the name, if no one registers, the original registrant can activate the name with any update pub const NAME_EXPIRE_SECONDS: u64 = NAME_STALE_SECONDS + 60 * 60 * 24 * 180; -#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct NameState { pub name: String, pub sequence: u64, @@ -20,9 +22,73 @@ pub struct NameState { pub expire_time: u64, pub threshold: u8, pub key_kind: u8, - pub public_keys: Vec>, - #[serde(skip_serializing_if = "Option::is_none")] - pub next_public_keys: Option>>, + pub public_keys: Vec, + pub next_public_keys: Option>, +} + +impl From<&NameState> for Value { + fn from(state: &NameState) -> Self { + let mut arr = vec![ + Value::from(state.name.clone()), + Value::from(state.sequence), + Value::from(state.block_height), + Value::from(state.block_time), + Value::from(state.stale_time), + Value::from(state.expire_time), + Value::from(state.threshold), + Value::from(state.key_kind), + Value::Array(state.public_keys.iter().map(Value::from).collect()), + ]; + if let Some(keys) = state.next_public_keys.as_ref() { + arr.push(Value::Array(keys.iter().map(Value::from).collect())); + } + Value::Array(arr) + } +} + +impl TryFrom<&Value> for NameState { + type Error = Error; + + fn try_from(value: &Value) -> Result { + let arr = value.as_array().ok_or_else(|| { + Error::Custom(format!( + "NameState: expected array, got {}", + kind_of_value(value) + )) + })?; + match arr.len() { + 9 | 10 => { + let mut state = NameState { + name: arr[0] + .as_text() + .ok_or_else(|| { + Error::Custom(format!( + "NameState: expected string, got {}", + kind_of_value(&arr[0]) + )) + })? + .to_string(), + sequence: u64::try_from(&IntValue(&arr[1]))?, + block_height: u64::try_from(&IntValue(&arr[2]))?, + block_time: u64::try_from(&IntValue(&arr[3]))?, + stale_time: u64::try_from(&IntValue(&arr[4]))?, + expire_time: u64::try_from(&IntValue(&arr[5]))?, + threshold: u8::try_from(&IntValue(&arr[6]))?, + key_kind: u8::try_from(&IntValue(&arr[7]))?, + public_keys: Bytes32::vec_try_from_value(&arr[8])?, + ..Default::default() + }; + if arr.len() == 10 { + state.next_public_keys = Some(Bytes32::vec_try_from_value(&arr[9])?); + } + Ok(state) + } + _ => Err(Error::Custom(format!( + "NameState: expected array of length 9 or 10, got {}", + arr.len() + ))), + } + } } impl NameState { @@ -34,7 +100,7 @@ impl NameState { } } - pub fn hash(&self) -> Result, Error> { + pub fn hash(&self) -> Result { hash_sha3(self) } @@ -143,7 +209,7 @@ impl NameState { } } -#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct ServiceState { pub name: String, pub code: u64, @@ -151,8 +217,74 @@ pub struct ServiceState { pub data: Vec<(u16, Value)>, } +impl From<&ServiceState> for Value { + fn from(state: &ServiceState) -> Self { + Value::Array(vec![ + Value::from(state.name.clone()), + Value::from(state.code), + Value::from(state.sequence), + Value::Map( + state + .data + .iter() + .map(|(subcode, params)| (Value::from(*subcode), params.clone())) + .collect(), + ), + ]) + } +} + +impl TryFrom<&Value> for ServiceState { + type Error = Error; + + fn try_from(value: &Value) -> Result { + let arr = value.as_array().ok_or_else(|| { + Error::Custom(format!( + "ServiceState: expected array, got {}", + kind_of_value(value) + )) + })?; + match arr.len() { + 4 => { + let state = ServiceState { + name: arr[0] + .as_text() + .ok_or_else(|| { + Error::Custom(format!( + "ServiceState: expected string, got {}", + kind_of_value(&arr[0]) + )) + })? + .to_string(), + code: u64::try_from(&IntValue(&arr[1]))?, + sequence: u64::try_from(&IntValue(&arr[2]))?, + data: arr[3] + .as_map() + .ok_or_else(|| { + Error::Custom(format!( + "ServiceState: expected map, got {}", + kind_of_value(&arr[3]) + )) + })? + .iter() + .map(|(k, v)| { + let subcode = u16::try_from(&IntValue(k))?; + Ok((subcode, v.clone())) + }) + .collect::, Error>>()?, + }; + Ok(state) + } + _ => Err(Error::Custom(format!( + "ServiceState: expected array of length 4, got {}", + arr.len() + ))), + } + } +} + impl ServiceState { - pub fn hash(&self) -> Result, Error> { + pub fn hash(&self) -> Result { hash_sha3(self) } @@ -199,7 +331,7 @@ impl ServiceState { } } -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct ServiceProtocol { pub code: u64, pub version: u16, @@ -220,8 +352,57 @@ impl Default for ServiceProtocol { } } +impl From<&ServiceProtocol> for Value { + fn from(state: &ServiceProtocol) -> Self { + Value::Array(vec![ + Value::from(state.code), + Value::from(state.version), + state.protocol.clone(), + Value::from(state.submitter.clone()), + Value::from(state.sequence), + ]) + } +} + +impl TryFrom<&Value> for ServiceProtocol { + type Error = Error; + + fn try_from(value: &Value) -> Result { + let arr = value.as_array().ok_or_else(|| { + Error::Custom(format!( + "ServiceProtocol: expected array, got {}", + kind_of_value(value) + )) + })?; + match arr.len() { + 5 => { + let state = ServiceProtocol { + code: u64::try_from(&IntValue(&arr[0]))?, + version: u16::try_from(&IntValue(&arr[1]))?, + protocol: arr[2].clone(), + submitter: arr[3] + .as_text() + .ok_or_else(|| { + Error::Custom(format!( + "ServiceProtocol: expected string, got {}", + kind_of_value(&arr[0]) + )) + })? + .to_string(), + sequence: u64::try_from(&IntValue(&arr[4]))?, + }; + Ok(state) + } + _ => Err(Error::Custom(format!( + "ServiceProtocol: expected array of length 4, got {}", + arr.len() + ))), + } + } +} + impl ServiceProtocol { - pub fn hash(&self) -> Result, Error> { + pub fn hash(&self) -> Result { hash_sha3(self) } @@ -244,26 +425,126 @@ impl ServiceProtocol { } } -#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct Inscription { pub name: String, pub sequence: u64, pub height: u64, pub name_height: u64, - pub previous_hash: Vec, - pub name_hash: Vec, - pub service_hash: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - pub protocol_hash: Option>, - pub block_hash: Vec, + pub previous_hash: Bytes32, + pub name_hash: Bytes32, + pub service_hash: Bytes32, + pub protocol_hash: Option, pub block_height: u64, - pub txid: Vec, + pub block_hash: Bytes32, + pub txid: Bytes32, pub vin: u8, pub data: Name, } +impl From<&Inscription> for Value { + fn from(state: &Inscription) -> Self { + let mut arr = vec![ + Value::from(state.name.clone()), + Value::from(state.sequence), + Value::from(state.height), + Value::Array(vec![ + Value::from(state.name_height), + Value::from(&state.previous_hash), + Value::from(&state.name_hash), + Value::from(&state.service_hash), + ]), + Value::Array(vec![ + Value::from(state.block_height), + Value::from(&state.block_hash), + Value::from(&state.txid), + Value::from(state.vin), + ]), + Value::from(&state.data), + ]; + if let Some(ref hash) = state.protocol_hash { + arr[3].as_array_mut().unwrap().push(Value::from(hash)); + } + Value::Array(arr) + } +} + +impl TryFrom<&Value> for Inscription { + type Error = Error; + + fn try_from(value: &Value) -> Result { + let arr = value.as_array().ok_or_else(|| { + Error::Custom(format!( + "Inscription: expected array, got {}", + kind_of_value(value) + )) + })?; + match arr.len() { + 6 => { + let ins_state = arr[3].as_array().ok_or_else(|| { + Error::Custom(format!( + "Inscription: expected array at 3, got {}", + kind_of_value(&arr[3]) + )) + })?; + if ins_state.len() != 4 && ins_state.len() != 5 { + return Err(Error::Custom(format!( + "Inscription: expected array of length 4 or 5 at 3, got {}", + ins_state.len() + ))); + } + let tx_state = arr[4].as_array().ok_or_else(|| { + Error::Custom(format!( + "Inscription: expected array at 4, got {}", + kind_of_value(&arr[3]) + )) + })?; + if tx_state.len() != 4 { + return Err(Error::Custom(format!( + "Inscription: expected array of length 4 at 4, got {}", + tx_state.len() + ))); + } + + let mut ins = Inscription { + name: arr[0] + .as_text() + .ok_or_else(|| { + Error::Custom(format!( + "Inscription: expected string, got {}", + kind_of_value(&arr[0]) + )) + })? + .to_string(), + sequence: u64::try_from(&IntValue(&arr[1]))?, + height: u64::try_from(&IntValue(&arr[2]))?, + name_height: u64::try_from(&IntValue(&ins_state[0]))?, + previous_hash: Bytes32::try_from(&ins_state[1])?, + name_hash: Bytes32::try_from(&ins_state[2])?, + service_hash: Bytes32::try_from(&ins_state[3])?, + protocol_hash: None, + block_height: u64::try_from(&IntValue(&tx_state[0]))?, + block_hash: Bytes32::try_from(&tx_state[1])?, + txid: Bytes32::try_from(&tx_state[2])?, + vin: u8::try_from(&IntValue(&tx_state[3]))?, + data: Name::try_from(&arr[5])?, + }; + if ins_state.len() == 5 { + ins.protocol_hash = Some(Bytes32::try_from(&ins_state[4])?); + } + + Ok(ins) + } + _ => Err(Error::Custom(format!( + "Inscription: expected array of length 4, got {}", + arr.len() + ))), + } + } +} + impl Inscription { - pub fn hash(&self) -> Result, Error> { + pub fn hash(&self) -> Result { hash_sha3(self) } } @@ -272,14 +553,86 @@ impl Inscription { pub struct InvalidInscription { pub name: String, pub block_height: u64, - pub hash: Vec, + pub hash: Bytes32, pub reason: String, pub data: Name, } -impl InvalidInscription { - pub fn hash(&self) -> Result, Error> { - hash_sha3(self) +impl InvalidInscription {} + +impl Serialize for NameState { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + Value::from(self).serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for NameState { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + let val = Value::deserialize(deserializer)?; + NameState::try_from(&val).map_err(de::Error::custom) + } +} + +impl Serialize for ServiceState { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + Value::from(self).serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for ServiceState { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + let val = Value::deserialize(deserializer)?; + ServiceState::try_from(&val).map_err(de::Error::custom) + } +} + +impl Serialize for ServiceProtocol { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + Value::from(self).serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for ServiceProtocol { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + let val = Value::deserialize(deserializer)?; + ServiceProtocol::try_from(&val).map_err(de::Error::custom) + } +} + +impl Serialize for Inscription { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + Value::from(self).serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for Inscription { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + let val = Value::deserialize(deserializer)?; + Inscription::try_from(&val).map_err(de::Error::custom) } } @@ -297,11 +650,11 @@ pub fn to_bytes(value: &T) -> Result, Error> { Ok(buf) } -pub fn hash_sha3(value: &T) -> Result, Error> { +pub fn hash_sha3(value: &T) -> Result { let mut hasher = Sha3_256::new(); into_writer(value, hasher.borrow_mut()) .map_err(|err| Error::Custom(format!("hash_sha3: {:?}", err)))?; - Ok(hasher.finalize().to_vec()) + Bytes32::try_from(hasher.finalize().as_slice()) } #[cfg(test)] @@ -329,7 +682,7 @@ mod tests { block_height: 1, block_time: 1, threshold: 1, - public_keys: vec![s1.verifying_key().to_bytes().to_vec()], + public_keys: vec![s1.verifying_key().to_bytes().into()], ..Default::default() }; @@ -342,8 +695,8 @@ mod tests { subcode: 1, params: ns::Value::from(&ns::PublicKeyParams { public_keys: vec![ - s1.verifying_key().to_bytes().to_vec(), - s2.verifying_key().to_bytes().to_vec(), + s1.verifying_key().to_bytes().into(), + s2.verifying_key().to_bytes().into(), ], threshold: None, kind: None, @@ -376,8 +729,8 @@ mod tests { subcode: 2, params: ns::Value::from(&ns::PublicKeyParams { public_keys: vec![ - s1.verifying_key().to_bytes().to_vec(), - s2.verifying_key().to_bytes().to_vec(), + s1.verifying_key().to_bytes().into(), + s2.verifying_key().to_bytes().into(), ], threshold: None, kind: None, @@ -405,13 +758,13 @@ mod tests { assert_eq!(1, name_state.threshold); assert_eq!(0, name_state.key_kind); assert_eq!( - vec![s1.verifying_key().to_bytes().to_vec()], + vec![Bytes32::from(s1.verifying_key().to_bytes())], name_state.public_keys ); assert_eq!( Some(vec![ - s1.verifying_key().to_bytes().to_vec(), - s2.verifying_key().to_bytes().to_vec() + s1.verifying_key().to_bytes().into(), + s2.verifying_key().to_bytes().into() ]), name_state.next_public_keys ); @@ -425,8 +778,8 @@ mod tests { subcode: 1, params: ns::Value::from(&ns::PublicKeyParams { public_keys: vec![ - s1.verifying_key().to_bytes().to_vec(), - s2.verifying_key().to_bytes().to_vec(), + s1.verifying_key().to_bytes().into(), + s2.verifying_key().to_bytes().into(), ], threshold: None, kind: None, @@ -454,8 +807,8 @@ mod tests { .sign( &ns::PublicKeyParams { public_keys: vec![ - s1.verifying_key().to_bytes().to_vec(), - s2.verifying_key().to_bytes().to_vec(), + s1.verifying_key().to_bytes().into(), + s2.verifying_key().to_bytes().into(), ], threshold: None, kind: None, @@ -476,8 +829,8 @@ mod tests { assert_eq!(0, name_state.key_kind); assert_eq!( vec![ - s1.verifying_key().to_bytes().to_vec(), - s2.verifying_key().to_bytes().to_vec() + Bytes32::from(s1.verifying_key().to_bytes()), + Bytes32::from(s2.verifying_key().to_bytes()), ], name_state.public_keys ); @@ -493,7 +846,7 @@ mod tests { ns::Operation { subcode: 2, params: ns::Value::from(&ns::PublicKeyParams { - public_keys: vec![s3.verifying_key().to_bytes().to_vec()], + public_keys: vec![s3.verifying_key().to_bytes().into()], threshold: None, kind: None, }), @@ -501,7 +854,7 @@ mod tests { ns::Operation { subcode: 1, params: ns::Value::from(&ns::PublicKeyParams { - public_keys: vec![s3.verifying_key().to_bytes().to_vec()], + public_keys: vec![s3.verifying_key().to_bytes().into()], threshold: None, kind: None, }), @@ -536,7 +889,7 @@ mod tests { assert_eq!(1, name_state.threshold); assert_eq!(0, name_state.key_kind); assert_eq!( - vec![s3.verifying_key().to_bytes().to_vec()], + vec![Bytes32::from(s3.verifying_key().to_bytes())], name_state.public_keys ); assert_eq!(None, name_state.next_public_keys); @@ -551,8 +904,8 @@ mod tests { subcode: 1, params: ns::Value::from(&ns::PublicKeyParams { public_keys: vec![ - s2.verifying_key().to_bytes().to_vec(), - s1.verifying_key().to_bytes().to_vec(), + s2.verifying_key().to_bytes().into(), + s1.verifying_key().to_bytes().into(), ], threshold: Some(1), kind: None, @@ -580,8 +933,8 @@ mod tests { assert_eq!(0, name_state.key_kind); assert_eq!( vec![ - s2.verifying_key().to_bytes().to_vec(), - s1.verifying_key().to_bytes().to_vec() + Bytes32::from(s2.verifying_key().to_bytes()), + Bytes32::from(s1.verifying_key().to_bytes()) ], name_state.public_keys ); @@ -597,7 +950,7 @@ mod tests { // this operation will be overwritten subcode: 2, params: ns::Value::from(&ns::PublicKeyParams { - public_keys: vec![s3.verifying_key().to_bytes().to_vec()], + public_keys: vec![s3.verifying_key().to_bytes().into()], threshold: None, kind: None, }), @@ -649,8 +1002,8 @@ mod tests { assert_eq!(1, name_state.threshold); assert_eq!( vec![ - s2.verifying_key().to_bytes().to_vec(), - s1.verifying_key().to_bytes().to_vec() + Bytes32::from(s2.verifying_key().to_bytes()), + Bytes32::from(s1.verifying_key().to_bytes()) ], name_state.public_keys ); @@ -666,7 +1019,7 @@ mod tests { // this operation will be overwritten subcode: 2, params: ns::Value::from(&ns::PublicKeyParams { - public_keys: vec![s3.verifying_key().to_bytes().to_vec()], + public_keys: vec![s3.verifying_key().to_bytes().into()], threshold: None, kind: None, }), @@ -718,12 +1071,16 @@ mod tests { assert_eq!(1, name_state.threshold); assert_eq!( vec![ - s2.verifying_key().to_bytes().to_vec(), - s1.verifying_key().to_bytes().to_vec() + Bytes32::from(s2.verifying_key().to_bytes()), + Bytes32::from(s1.verifying_key().to_bytes()) ], name_state.public_keys ); assert_eq!(None, name_state.next_public_keys); + + let mut data: Vec = Vec::new(); + into_writer(&name_state, &mut data).unwrap(); + println!("name_state: {:?}", hex::encode(&data)); } #[test]