diff --git a/crates/ns-fetcher/src/fetcher.rs b/crates/ns-fetcher/src/fetcher.rs index fb8a7b3..653e5fe 100644 --- a/crates/ns-fetcher/src/fetcher.rs +++ b/crates/ns-fetcher/src/fetcher.rs @@ -2,15 +2,20 @@ use async_stream::try_stream; use bloomfilter::Bloom; use futures_core::stream::Stream; -use ns_protocol::state::{Inscription, NameState, ServiceState}; +use ns_protocol::state::{Inscription, NameState, ServiceProtocol, ServiceState}; use crate::indexer::Client; +pub type InscriptionState = ( + Inscription, + Option<(NameState, ServiceState, Option)>, +); + // fetches all inscriptions and states from last accepted to bottom_height pub fn fetch_desc( cli: Client, bottom_height: u64, -) -> impl Stream)>> { +) -> impl Stream> { try_stream! { let last_accepted: Inscription = cli.get_last_accepted_inscription().await?; let name_state: NameState = cli.get_name_state(&last_accepted.name).await?; @@ -21,7 +26,7 @@ pub fn fetch_desc( let mut head_inscription = last_accepted.clone(); bloom.set(&head_inscription.name); - yield (last_accepted, Some((name_state, service_state))); + yield (last_accepted, Some((name_state, service_state, None))); loop { if head_height == 0 || head_height < bottom_height { @@ -65,7 +70,7 @@ pub fn fetch_desc( Err(anyhow::anyhow!("inscription({}): service_hash mismatch", inscription.height))?; } - yield (inscription, Some((name_state, service_state))); + yield (inscription, Some((name_state, service_state, None))); } } } @@ -81,7 +86,7 @@ mod tests { #[ignore] async fn fetcher_works() { let endpoint = std::env::var("INDEXER_ENDPOINT").unwrap_or_default(); - // let endpoint = "http://127.0.0.1::8080".to_string(); + // let endpoint = "http://192.168.1.80:8080".to_string(); if endpoint.is_empty() { return; } @@ -95,7 +100,7 @@ mod tests { let (last_accepted, state) = s.next().await.unwrap().unwrap(); assert!(last_accepted.height > 0); assert!(state.is_some()); - let (name_state, service_state) = state.unwrap(); + let (name_state, service_state, _) = state.unwrap(); assert_eq!(last_accepted.name, name_state.name); assert_eq!(last_accepted.sequence, name_state.sequence); assert_eq!(last_accepted.name, service_state.name); @@ -104,10 +109,13 @@ mod tests { assert_eq!(last_accepted.service_hash, service_state.hash().unwrap()); let mut state_exists = false; + let mut head_ins = last_accepted.clone(); while let Some(res) = s.next().await { let (ins, state) = res.unwrap(); println!("got {}, {}, {}", ins.height, ins.name, ins.sequence); - if let Some((name_state, service_state)) = state { + assert_eq!(head_ins.previous_hash, ins.hash().unwrap()); + head_ins = ins.clone(); + if let Some((name_state, service_state, _)) = state { assert_eq!(ins.name, name_state.name); assert_eq!(ins.sequence, name_state.sequence); assert_eq!(ins.name, service_state.name); diff --git a/crates/ns-indexer/src/db/model_inscription.rs b/crates/ns-indexer/src/db/model_inscription.rs index 945a060..d360bef 100644 --- a/crates/ns-indexer/src/db/model_inscription.rs +++ b/crates/ns-indexer/src/db/model_inscription.rs @@ -396,7 +396,19 @@ impl Inscription { ); } - let _ = db.batch(statements, values).await?; + let mut start = 0; + while start < statements.len() { + let end = if start + 1000 > statements.len() { + statements.len() + } else { + start + 1000 + }; + + let _ = db + .batch(statements[start..end].to_vec(), &values[start..end]) + .await?; + start = end; + } Ok(()) } diff --git a/crates/ns-indexer/src/db/model_name_state.rs b/crates/ns-indexer/src/db/model_name_state.rs index 2cf3a1f..4adbfc8 100644 --- a/crates/ns-indexer/src/db/model_name_state.rs +++ b/crates/ns-indexer/src/db/model_name_state.rs @@ -134,33 +134,43 @@ impl NameState { db: &scylladb::ScyllaDB, names: Vec<&String>, ) -> anyhow::Result> { - let mut vals_name: Vec<&str> = Vec::with_capacity(names.len()); - let mut params: Vec = Vec::with_capacity(names.len()); + let fields = vec!["name".to_string(), "public_keys".to_string()]; - for name in names { - vals_name.push("?"); - params.push(name.to_cql()); - } + let mut output: Vec = Vec::with_capacity(names.len()); - let fields = vec!["name".to_string(), "public_keys".to_string()]; - let query = format!( - "SELECT {} FROM name_state WHERE name IN ({})", - fields.join(","), - vals_name.join(",") - ); - let res = db.execute(query, params).await?; - - let rows = res.rows.unwrap_or_default(); - let mut res: Vec = Vec::with_capacity(rows.len()); - for r in rows { - let mut cols = ColumnsMap::with_capacity(2); - cols.fill(r, &fields)?; - let mut item = NameState::default(); - item.fill(&cols); - res.push(item); + let mut start = 0; + while start < names.len() { + let end = if start + 100 > names.len() { + names.len() + } else { + start + 100 + }; + + let mut vals_name: Vec<&str> = Vec::with_capacity(end - start); + let mut params: Vec = Vec::with_capacity(end - start); + for name in &names[start..end] { + vals_name.push("?"); + params.push(name.to_cql()); + } + + let query = format!( + "SELECT {} FROM name_state WHERE name IN ({})", + fields.join(","), + vals_name.join(",") + ); + let res = db.execute(query, params).await?; + let rows = res.rows.unwrap_or_default(); + for r in rows { + let mut cols = ColumnsMap::with_capacity(2); + cols.fill(r, &fields)?; + let mut item = NameState::default(); + item.fill(&cols); + output.push(item); + } + start = end; } - Ok(res) + Ok(output) } pub async fn batch_update_name_indexs( diff --git a/crates/ns-protocol/Cargo.toml b/crates/ns-protocol/Cargo.toml index 8667d71..ca78524 100644 --- a/crates/ns-protocol/Cargo.toml +++ b/crates/ns-protocol/Cargo.toml @@ -24,3 +24,4 @@ sha3 = "0.10" [dev-dependencies] hex = "0.4" hex-literal = "0.4" +rand_core = { version = "0.6", features = ["getrandom", "alloc"] } diff --git a/crates/ns-protocol/src/ns.rs b/crates/ns-protocol/src/ns.rs index e2dcdbd..24756bc 100644 --- a/crates/ns-protocol/src/ns.rs +++ b/crates/ns-protocol/src/ns.rs @@ -107,9 +107,9 @@ impl PublicKeyParams { let mut public_keys = self.public_keys.clone(); public_keys.dedup(); if public_keys.len() != self.public_keys.len() { - return Err(Error::Custom(format!( - "PublicKeyParams: duplicate public_keys", - ))); + return Err(Error::Custom( + "PublicKeyParams: duplicate public_keys".to_string(), + )); } Ok(()) @@ -261,7 +261,7 @@ impl Name { let mut signatures = self.signatures.clone(); signatures.dedup(); if signatures.len() != self.signatures.len() { - return Err(Error::Custom(format!("Name: duplicate signatures",))); + return Err(Error::Custom("Name: duplicate signatures".to_string())); } Ok(()) } @@ -276,15 +276,19 @@ impl Name { } let data = self.to_sign_bytes()?; - let mut keys = params.public_keys.iter(); + 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()) + .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()))?; - for key in keys.by_ref() { - let verifying_key = ed25519::VerifyingKey::try_from(key.as_slice()) - .map_err(|err| Error::Custom(err.to_string()))?; - if verifying_key.verify_strict(&data, &sig).is_ok() { + for key in keys.iter() { + if key.verify_strict(&data, &sig).is_ok() { count += 1; if count >= threshold { return Ok(()); @@ -324,8 +328,12 @@ impl Name { { let sig = Signature(signer.sign(&data).to_bytes().to_vec()); self.signatures.push(sig); + if self.signatures.len() == threshold as usize { + break; + } } } + if self.signatures.len() != threshold as usize { return Err(Error::Custom(format!( "Name: expected {} signatures, got {}", @@ -742,6 +750,13 @@ fn kind_of_value(v: &Value) -> String { mod tests { use super::*; use hex_literal::hex; + use rand_core::{OsRng, RngCore}; + + fn secret_key() -> [u8; 32] { + let mut data = [0u8; 32]; + OsRng.fill_bytes(&mut data); + data + } #[test] fn valid_name_works() { @@ -853,4 +868,106 @@ mod tests { buf.push(0x80); assert!(Name::from_bytes(&buf[..]).is_err()); } + + #[test] + fn name_sign_verify() { + let s1 = ed25519::SigningKey::from_bytes(&secret_key()); + let s2 = ed25519::SigningKey::from_bytes(&secret_key()); + let s3 = ed25519::SigningKey::from_bytes(&secret_key()); + let s4 = ed25519::SigningKey::from_bytes(&secret_key()); + let mut signers = vec![s1.clone(), s2.clone(), s3.clone(), s4.clone()]; + + 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(), + ], + threshold: Some(2), + kind: None, + }; + + let mut name = Name { + name: "道".to_string(), + sequence: 0, + payload: Service { + code: 0, + operations: vec![Operation { + subcode: 1, + params: Value::from(¶ms), + }], + approver: None, + }, + signatures: vec![], + }; + assert!(name.validate().is_err()); + name.sign(¶ms, ThresholdLevel::Single, &signers) + .unwrap(); + assert!(name.validate().is_ok()); + assert_eq!(1, name.signatures.len()); + assert!(name.verify(¶ms, ThresholdLevel::Single).is_ok()); + assert!(name.verify(¶ms, ThresholdLevel::Default).is_err()); + assert!(name.verify(¶ms, ThresholdLevel::Strict).is_err()); + assert!(name.verify(¶ms, ThresholdLevel::All).is_err()); + + name.sign(¶ms, ThresholdLevel::Default, &signers) + .unwrap(); + assert!(name.validate().is_ok()); + assert_eq!(2, name.signatures.len()); + assert!(name.verify(¶ms, ThresholdLevel::Single).is_ok()); + assert!(name.verify(¶ms, ThresholdLevel::Default).is_ok()); + assert!(name.verify(¶ms, ThresholdLevel::Strict).is_err()); + assert!(name.verify(¶ms, ThresholdLevel::All).is_err()); + + name.sign(¶ms, ThresholdLevel::Strict, &signers) + .unwrap(); + assert!(name.validate().is_ok()); + assert_eq!(3, name.signatures.len()); + assert!(name.verify(¶ms, ThresholdLevel::Single).is_ok()); + assert!(name.verify(¶ms, ThresholdLevel::Default).is_ok()); + assert!(name.verify(¶ms, ThresholdLevel::Strict).is_ok()); + assert!(name.verify(¶ms, ThresholdLevel::All).is_err()); + + name.sign(¶ms, ThresholdLevel::All, &signers).unwrap(); + assert!(name.validate().is_ok()); + assert_eq!(4, name.signatures.len()); + assert!(name.verify(¶ms, ThresholdLevel::Single).is_ok()); + 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!( + name.sign(¶ms, ThresholdLevel::All, &signers[1..]) + .is_err(), + "signers less than ThresholdLevel::All" + ); + assert!(name + .sign(¶ms, ThresholdLevel::Strict, &signers[1..]) + .is_ok()); + assert!(name.verify(¶ms, ThresholdLevel::Strict).is_ok()); + assert!(name.verify(¶ms, ThresholdLevel::All).is_err()); + + signers[3] = s3.clone(); + assert!( + name.sign(¶ms, ThresholdLevel::All, &signers).is_err(), + "depulicate signer" + ); + assert!(name.sign(¶ms, ThresholdLevel::Strict, &signers).is_ok()); + assert!(name.verify(¶ms, ThresholdLevel::Strict).is_ok()); + assert!(name.verify(¶ms, ThresholdLevel::All).is_err()); + + signers[3] = ed25519::SigningKey::from_bytes(&secret_key()); + assert!( + name.sign(¶ms, ThresholdLevel::All, &signers).is_err(), + "stranger signer" + ); + assert!(name.sign(¶ms, ThresholdLevel::Strict, &signers).is_ok()); + assert!(name.verify(¶ms, ThresholdLevel::Strict).is_ok()); + assert!(name.verify(¶ms, ThresholdLevel::All).is_err()); + + signers[3] = s4.clone(); + assert!(name.sign(¶ms, ThresholdLevel::All, &signers).is_ok()); + assert!(name.verify(¶ms, ThresholdLevel::All).is_ok()); + } } diff --git a/crates/ns-protocol/src/state.rs b/crates/ns-protocol/src/state.rs index a179fcc..35a8fde 100644 --- a/crates/ns-protocol/src/state.rs +++ b/crates/ns-protocol/src/state.rs @@ -37,7 +37,7 @@ impl NameState { } pub fn is_stale(&self, block_time: u64) -> bool { - return self.block_time + NAME_STALE_SECONDS < block_time; + self.block_time + NAME_STALE_SECONDS < block_time } pub fn verify_the_next( @@ -96,17 +96,17 @@ impl NameState { sequence: next.sequence, block_height, block_time, - threshold: self.threshold, - key_kind: self.key_kind, - public_keys: self.public_keys.clone(), + threshold: next_state.threshold, + key_kind: next_state.key_kind, + public_keys: next_state.public_keys.clone(), next_public_keys: Some(public_key_params.public_keys), }; } 1 => { // update public_keys - let allow_update = (self.block_time + NAME_EXPIRE_SECONDS < block_time) - || (self.next_public_keys.is_some() - && self.next_public_keys.as_ref().unwrap() + let allow_update = (next_state.block_time + NAME_EXPIRE_SECONDS < block_time) + || (next_state.next_public_keys.is_some() + && next_state.next_public_keys.as_ref().unwrap() == &public_key_params.public_keys); if !allow_update { @@ -296,3 +296,525 @@ pub fn hash_sha3(value: &T) -> Result, Error> { .map_err(|err| Error::Custom(format!("hash_sha3: {:?}", err)))?; Ok(hasher.finalize().to_vec()) } + +#[cfg(test)] +mod tests { + use super::*; + use rand_core::{OsRng, RngCore}; + + use crate::{ed25519, ns}; + + fn secret_key() -> [u8; 32] { + let mut data = [0u8; 32]; + OsRng.fill_bytes(&mut data); + data + } + + #[test] + fn name_state_works() { + let s1 = ed25519::SigningKey::from_bytes(&secret_key()); + let s2 = ed25519::SigningKey::from_bytes(&secret_key()); + let s3 = ed25519::SigningKey::from_bytes(&secret_key()); + + let name_state = NameState { + name: "test".to_string(), + sequence: 0, + block_height: 1, + block_time: 1, + threshold: 1, + key_kind: 0, + public_keys: vec![s1.verifying_key().to_bytes().to_vec()], + next_public_keys: None, + }; + + let mut next_name = ns::Name { + name: "test".to_string(), + sequence: 1, + payload: ns::Service { + code: 0, + operations: vec![ns::Operation { + 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(), + ], + threshold: None, + kind: None, + }), + }], + approver: None, + }, + signatures: vec![], + }; + next_name + .sign( + &name_state.public_key_params(), + ns::ThresholdLevel::Default, + &[s1.clone()], + ) + .unwrap(); + next_name.validate().unwrap(); + + assert!( + name_state.verify_the_next(3, 3, &next_name).is_err(), + "do not allow update" + ); + + let mut next_name = ns::Name { + name: "test".to_string(), + sequence: 1, + payload: ns::Service { + code: 0, + operations: vec![ns::Operation { + 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(), + ], + threshold: None, + kind: None, + }), + }], + approver: None, + }, + signatures: vec![], + }; + next_name + .sign( + &name_state.public_key_params(), + ns::ThresholdLevel::Default, + &[s1.clone()], + ) + .unwrap(); + next_name.validate().unwrap(); + + let name_state = name_state.verify_the_next(3, 3, &next_name).unwrap(); + assert_eq!(1, name_state.sequence); + assert_eq!(3, name_state.block_height); + assert_eq!(3, name_state.block_time); + assert_eq!(1, name_state.threshold); + assert_eq!(0, name_state.key_kind); + assert_eq!( + vec![s1.verifying_key().to_bytes().to_vec()], + name_state.public_keys + ); + assert_eq!( + Some(vec![ + s1.verifying_key().to_bytes().to_vec(), + s2.verifying_key().to_bytes().to_vec() + ]), + name_state.next_public_keys + ); + + let mut next_name = ns::Name { + name: "test".to_string(), + sequence: 2, + payload: ns::Service { + code: 0, + operations: vec![ns::Operation { + 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(), + ], + threshold: None, + kind: None, + }), + }], + approver: None, + }, + signatures: vec![], + }; + next_name + .sign( + &name_state.public_key_params(), + ns::ThresholdLevel::Default, + &[s1.clone()], + ) + .unwrap(); + next_name.validate().unwrap(); + + assert!( + name_state.verify_the_next(5, 5, &next_name).is_err(), + "invalid signatures" + ); + + next_name + .sign( + &ns::PublicKeyParams { + public_keys: vec![ + s1.verifying_key().to_bytes().to_vec(), + s2.verifying_key().to_bytes().to_vec(), + ], + threshold: None, + kind: None, + }, + ns::ThresholdLevel::All, + &[s1.clone(), s2.clone()], + ) + .unwrap(); + next_name.validate().unwrap(); + + let name_state = name_state.verify_the_next(5, 5, &next_name).unwrap(); + assert_eq!(2, name_state.sequence); + assert_eq!(5, name_state.block_height); + assert_eq!(5, name_state.block_time); + assert_eq!(2, name_state.threshold); + assert_eq!(0, name_state.key_kind); + assert_eq!( + vec![ + s1.verifying_key().to_bytes().to_vec(), + s2.verifying_key().to_bytes().to_vec() + ], + name_state.public_keys + ); + assert_eq!(None, name_state.next_public_keys); + + // update public_keys in one call + let mut next_name = ns::Name { + name: "test".to_string(), + sequence: 3, + payload: ns::Service { + code: 0, + operations: vec![ + ns::Operation { + subcode: 2, + params: ns::Value::from(&ns::PublicKeyParams { + public_keys: vec![s3.verifying_key().to_bytes().to_vec()], + threshold: None, + kind: None, + }), + }, + ns::Operation { + subcode: 1, + params: ns::Value::from(&ns::PublicKeyParams { + public_keys: vec![s3.verifying_key().to_bytes().to_vec()], + threshold: None, + kind: None, + }), + }, + ], + approver: None, + }, + signatures: vec![], + }; + next_name + .sign( + &name_state.public_key_params(), + ns::ThresholdLevel::Default, + &[s1.clone(), s2.clone()], + ) + .unwrap(); + + next_name.validate().unwrap(); + assert!( + name_state.verify_the_next(7, 7, &next_name).is_err(), + "invalid signatures" + ); + + next_name.sign_with(&s3).unwrap(); + assert_eq!(3, next_name.signatures.len()); + let name_state = name_state.verify_the_next(7, 7, &next_name).unwrap(); + assert_eq!(3, name_state.sequence); + assert_eq!(7, name_state.block_height); + assert_eq!(7, name_state.block_time); + assert_eq!(1, name_state.threshold); + assert_eq!(0, name_state.key_kind); + assert_eq!( + vec![s3.verifying_key().to_bytes().to_vec()], + name_state.public_keys + ); + assert_eq!(None, name_state.next_public_keys); + + // update public_keys after NAME_EXPIRE_SECONDS + let mut next_name = ns::Name { + name: "test".to_string(), + sequence: 4, + payload: ns::Service { + code: 0, + operations: vec![ns::Operation { + 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(), + ], + threshold: Some(1), + kind: None, + }), + }], + approver: None, + }, + signatures: vec![], + }; + + next_name.sign_with(&s1).unwrap(); + next_name.sign_with(&s2).unwrap(); + next_name.validate().unwrap(); + + let name_state = name_state + .verify_the_next(8, 8 + NAME_EXPIRE_SECONDS, &next_name) + .unwrap(); + assert_eq!(4, name_state.sequence); + assert_eq!(8, name_state.block_height); + assert_eq!(8 + NAME_EXPIRE_SECONDS, name_state.block_time); + assert_eq!(1, name_state.threshold); + assert_eq!(0, name_state.key_kind); + assert_eq!( + vec![ + s2.verifying_key().to_bytes().to_vec(), + s1.verifying_key().to_bytes().to_vec() + ], + name_state.public_keys + ); + assert_eq!(None, name_state.next_public_keys); + + // the lightweight update operation + let mut next_name = ns::Name { + name: "test".to_string(), + sequence: 5, + payload: ns::Service { + code: 0, + operations: vec![ns::Operation { + // this operation will be overwritten + subcode: 2, + params: ns::Value::from(&ns::PublicKeyParams { + public_keys: vec![s3.verifying_key().to_bytes().to_vec()], + threshold: None, + kind: None, + }), + }], + approver: None, + }, + signatures: vec![], + }; + next_name + .sign( + &name_state.public_key_params(), + ns::ThresholdLevel::Strict, + &[s1.clone(), s2.clone()], + ) + .unwrap(); + + next_name.validate().unwrap(); + let name_state = name_state + .verify_the_next(name_state.block_height, name_state.block_time, &next_name) + .unwrap(); + assert!(name_state.next_public_keys.is_some()); + + let mut next_name = ns::Name { + name: "test".to_string(), + sequence: 6, + payload: ns::Service { + code: 0, + operations: vec![ns::Operation { + subcode: 0, + params: ns::Value::Null, + }], + approver: None, + }, + signatures: vec![], + }; + next_name + .sign( + &name_state.public_key_params(), + ns::ThresholdLevel::Default, + &[s1.clone()], + ) + .unwrap(); + + next_name.validate().unwrap(); + let name_state = name_state + .verify_the_next(name_state.block_height, name_state.block_time, &next_name) + .unwrap(); + assert_eq!(6, name_state.sequence); + assert_eq!(1, name_state.threshold); + assert_eq!( + vec![ + s2.verifying_key().to_bytes().to_vec(), + s1.verifying_key().to_bytes().to_vec() + ], + name_state.public_keys + ); + assert_eq!(None, name_state.next_public_keys); + + // the other update operation + let mut next_name = ns::Name { + name: "test".to_string(), + sequence: 7, + payload: ns::Service { + code: 0, + operations: vec![ns::Operation { + // this operation will be overwritten + subcode: 2, + params: ns::Value::from(&ns::PublicKeyParams { + public_keys: vec![s3.verifying_key().to_bytes().to_vec()], + threshold: None, + kind: None, + }), + }], + approver: None, + }, + signatures: vec![], + }; + next_name + .sign( + &name_state.public_key_params(), + ns::ThresholdLevel::Strict, + &[s2.clone(), s1.clone()], + ) + .unwrap(); + + next_name.validate().unwrap(); + let name_state = name_state + .verify_the_next(name_state.block_height, name_state.block_time, &next_name) + .unwrap(); + assert!(name_state.next_public_keys.is_some()); + + let mut next_name = ns::Name { + name: "test".to_string(), + sequence: 8, + payload: ns::Service { + code: 123, + operations: vec![ns::Operation { + subcode: 0, + params: ns::Value::Null, + }], + approver: None, + }, + signatures: vec![], + }; + next_name + .sign( + &name_state.public_key_params(), + ns::ThresholdLevel::Default, + &[s1.clone()], + ) + .unwrap(); + + next_name.validate().unwrap(); + let name_state = name_state + .verify_the_next(name_state.block_height, name_state.block_time, &next_name) + .unwrap(); + assert_eq!(8, name_state.sequence); + assert_eq!(1, name_state.threshold); + assert_eq!( + vec![ + s2.verifying_key().to_bytes().to_vec(), + s1.verifying_key().to_bytes().to_vec() + ], + name_state.public_keys + ); + assert_eq!(None, name_state.next_public_keys); + } + + #[test] + fn service_state_works() { + let mut service_state = ServiceState { + name: "test".to_string(), + code: 0, + sequence: 0, + data: Vec::new(), + }; + let next_name = ns::Name { + name: "test".to_string(), + sequence: 1, + payload: ns::Service { + code: 0, + operations: vec![ns::Operation { + subcode: 0, + params: ns::Value::Null, + }], + approver: None, + }, + signatures: vec![], + }; + + service_state = service_state.verify_the_next(&next_name).unwrap(); + assert_eq!(1, service_state.sequence); + assert_eq!(1, service_state.data.len()); + assert_eq!(0, service_state.data[0].0); + assert_eq!(ns::Value::Null, service_state.data[0].1); + + let next_name = ns::Name { + name: "test".to_string(), + sequence: 2, + payload: ns::Service { + code: 0, + operations: vec![ns::Operation { + subcode: 0, + params: ns::Value::Text("hello".to_string()), + }], + approver: None, + }, + signatures: vec![], + }; + + service_state = service_state.verify_the_next(&next_name).unwrap(); + assert_eq!(2, service_state.sequence); + assert_eq!(1, service_state.data.len()); + assert_eq!(0, service_state.data[0].0); + assert_eq!( + ns::Value::Text("hello".to_string()), + service_state.data[0].1 + ); + + let next_name = ns::Name { + name: "test".to_string(), + sequence: 3, + payload: ns::Service { + code: 0, + operations: vec![ns::Operation { + subcode: 3, + params: ns::Value::Null, + }], + approver: None, + }, + signatures: vec![], + }; + + service_state = service_state.verify_the_next(&next_name).unwrap(); + assert_eq!(3, service_state.sequence); + assert_eq!(2, service_state.data.len()); + assert_eq!(0, service_state.data[0].0); + assert_eq!( + ns::Value::Text("hello".to_string()), + service_state.data[0].1 + ); + assert_eq!(3, service_state.data[1].0); + assert_eq!(ns::Value::Null, service_state.data[1].1); + + let next_name = ns::Name { + name: "test".to_string(), + sequence: 4, + payload: ns::Service { + code: 0, + operations: vec![ns::Operation { + subcode: 2, + params: ns::Value::Text("hello2".to_string()), + }], + approver: None, + }, + signatures: vec![], + }; + + service_state = service_state.verify_the_next(&next_name).unwrap(); + assert_eq!(4, service_state.sequence); + assert_eq!(3, service_state.data.len()); + assert_eq!(0, service_state.data[0].0); + assert_eq!( + ns::Value::Text("hello".to_string()), + service_state.data[0].1 + ); + assert_eq!(2, service_state.data[1].0); + assert_eq!( + ns::Value::Text("hello2".to_string()), + service_state.data[1].1 + ); + assert_eq!(3, service_state.data[2].0); + assert_eq!(ns::Value::Null, service_state.data[2].1); + } +}