Skip to content

Commit

Permalink
refactor PSKB as a type wrapper + update serialization (#86)
Browse files Browse the repository at this point in the history
  • Loading branch information
aspect authored Aug 4, 2024
1 parent ba31ab9 commit 78adc19
Show file tree
Hide file tree
Showing 11 changed files with 73 additions and 106 deletions.
23 changes: 0 additions & 23 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@ regex = "1.10.2"
ripemd = { version = "0.1.3", default-features = false }
rlimit = "0.10.1"
rocksdb = "0.21.0"
rmp-serde = "1.3.0"
secp256k1 = { version = "0.28.2", features = [
"global-context",
"rand-std",
Expand Down
8 changes: 4 additions & 4 deletions cli/src/modules/pskb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl Pskb {
)
.await?;

match signer.to_hex() {
match signer.serialize() {
Ok(encoded) => tprintln!(ctx, "{encoded}"),
Err(e) => return Err(e.into()),
}
Expand Down Expand Up @@ -102,7 +102,7 @@ impl Pskb {
)
.await?;

match signer.to_hex() {
match signer.serialize() {
Ok(encoded) => tprintln!(ctx, "{encoded}"),
Err(e) => return Err(e.into()),
}
Expand Down Expand Up @@ -138,7 +138,7 @@ impl Pskb {
// Sweep UTXO set.
match unlock_utxos_as_pskb(references, &receive_address, script_sig, priority_fee_sompi as u64) {
Ok(pskb) => {
let pskb_hex = pskb.to_hex()?;
let pskb_hex = pskb.serialize()?;
tprintln!(ctx, "{pskb_hex}");
}
Err(e) => tprintln!(ctx, "Error generating unlock PSKB: {}", e.to_string()),
Expand Down Expand Up @@ -199,7 +199,7 @@ impl Pskb {
// Debug bundle view.
tprintln!(ctx, "{:?}", pskb);

match pskb.inner_list.first() {
match pskb.as_ref().first() {
Some(bundle_inner) => {
let pskt: PSKT<Signer> = PSKT::<Signer>::from(bundle_inner.to_owned());
let mut fin = pskt.finalizer();
Expand Down
5 changes: 2 additions & 3 deletions wallet/core/src/account/pskb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ pub async fn pskb_signer_for_address(
let addresses: Vec<Address> = match sign_for_address {
Some(signer) => vec![signer.clone()],
None => bundle
.inner_list
.iter()
.flat_map(|inner| {
inner.inputs
Expand All @@ -179,7 +178,7 @@ pub async fn pskb_signer_for_address(
// Prepare the signer.
signer.ingest(addresses.as_ref())?;

for pskt_inner in bundle.inner_list.clone() {
for pskt_inner in bundle.iter().cloned() {
let pskt: PSKT<Signer> = PSKT::from(pskt_inner);

let mut sign = |signer_pskt: PSKT<Signer>| {
Expand Down Expand Up @@ -272,7 +271,7 @@ pub fn finalize_pskt_no_sig_and_redeem_script(pskt: PSKT<Finalizer>) -> Result<P
}

pub fn bundle_to_finalizer_stream(bundle: &Bundle) -> impl Stream<Item = Result<PSKT<Finalizer>, Error>> + Send {
stream::iter(bundle.inner_list.clone()).map(move |pskt_inner| {
stream::iter(bundle.iter().cloned().collect::<Vec<_>>()).map(move |pskt_inner| {
let pskt: PSKT<Creator> = PSKT::from(pskt_inner);
let pskt_finalizer = pskt.constructor().updater().signer().finalizer();
finalize_pskt_one_or_more_sig_and_redeem_script(pskt_finalizer)
Expand Down
1 change: 0 additions & 1 deletion wallet/pskt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ js-sys.workspace = true
futures.workspace = true
hex.workspace = true
secp256k1.workspace = true
rmp-serde.workspace = true
serde_repr.workspace = true
serde-value.workspace = true
serde.workspace = true
Expand Down
117 changes: 43 additions & 74 deletions wallet/pskt/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,116 +12,85 @@ use serde::{Deserialize, Serialize};
use std::ops::Deref;

#[derive(Debug, Serialize, Deserialize)]
pub struct Bundle {
pub inner_list: Vec<PSKTInner>,
}
#[serde(rename_all = "camelCase")]
pub struct Bundle(pub Vec<PSKTInner>);

impl<ROLE> From<PSKT<ROLE>> for Bundle {
fn from(pskt: PSKT<ROLE>) -> Self {
Bundle { inner_list: vec![pskt.deref().clone()] }
Bundle(vec![pskt.deref().clone()])
}
}

impl<ROLE> From<Vec<PSKT<ROLE>>> for Bundle {
fn from(pskts: Vec<PSKT<ROLE>>) -> Self {
let inner_list = pskts.into_iter().map(|pskt| pskt.deref().clone()).collect();
Bundle { inner_list }
Bundle(inner_list)
}
}

impl Bundle {
pub fn new() -> Self {
Self { inner_list: Vec::new() }
Self(Vec::new())
}

/// Adds an Inner instance to the bundle
pub fn add_inner(&mut self, inner: PSKTInner) {
self.inner_list.push(inner);
self.0.push(inner);
}

/// Adds a PSKT instance to the bundle
pub fn add_pskt<ROLE>(&mut self, pskt: PSKT<ROLE>) {
self.inner_list.push(pskt.deref().clone());
self.0.push(pskt.deref().clone());
}

/// Merges another bundle into the current bundle
pub fn merge(&mut self, other: Bundle) {
for inner in other.inner_list {
self.inner_list.push(inner);
}
}

pub fn to_hex(&self) -> Result<String, Error> {
match TypeMarked::new(self, Marker::Pskb) {
Ok(type_marked) => match serde_json::to_string(&type_marked) {
Ok(result) => Ok(hex::encode(result)),
Err(e) => Err(Error::PskbSerializeToHexError(e.to_string())),
},
Err(e) => Err(Error::PskbSerializeToHexError(e.to_string())),
for inner in other.0 {
self.0.push(inner);
}
}

pub fn from_hex(hex_data: &str) -> Result<Self, Error> {
let bundle: TypeMarked<Bundle> = serde_json::from_slice(hex::decode(hex_data)?.as_slice())?;
Ok(bundle.data)
/// Iterator over the inner PSKT instances
pub fn iter(&self) -> std::slice::Iter<PSKTInner> {
self.0.iter()
}
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum Marker {
Pskb,
}

impl Marker {
fn as_str(&self) -> &str {
match self {
Marker::Pskb => "pskb",
}
pub fn serialize(&self) -> Result<String, Error> {
Ok(format!("PSKB{}", hex::encode(serde_json::to_string(self)?)))
}

fn from_str(marker: &str) -> Result<Self, Error> {
match marker {
"pskb" => Ok(Marker::Pskb),
_ => Err("Invalid pskb type marker".into()),
pub fn deserialize(hex_data: &str) -> Result<Self, Error> {
if let Some(hex_data) = hex_data.strip_prefix("PSKB") {
Ok(serde_json::from_slice(hex::decode(hex_data)?.as_slice())?)
} else {
Err(Error::PskbPrefixError)
}
}
}

#[derive(Serialize, Deserialize, Debug)]
struct TypeMarked<T> {
type_marker: String,
#[serde(flatten)]
data: T,
}

impl<T> TypeMarked<T> {
fn new(data: T, marker: Marker) -> Result<Self, Error> {
let type_marker = marker.as_str().to_string();
if Marker::from_str(&type_marker)? == marker {
Ok(Self { type_marker, data })
} else {
Err("Invalid pskb type marker".into())
}
impl AsRef<[PSKTInner]> for Bundle {
fn as_ref(&self) -> &[PSKTInner] {
self.0.as_slice()
}
}

impl TryFrom<String> for Bundle {
type Error = Error;
fn try_from(value: String) -> Result<Self, Error> {
Bundle::from_hex(&value)
Bundle::deserialize(&value)
}
}

impl TryFrom<&str> for Bundle {
type Error = Error;
fn try_from(value: &str) -> Result<Self, Error> {
Bundle::from_hex(value)
Bundle::deserialize(value)
}
}
impl TryFrom<Bundle> for String {
type Error = Error;
fn try_from(value: Bundle) -> Result<String, Error> {
match Bundle::to_hex(&value) {
match Bundle::serialize(&value) {
Ok(output) => Ok(output.to_owned()),
Err(e) => Err(Error::PskbSerializeError(e.to_string())),
}
Expand Down Expand Up @@ -232,7 +201,6 @@ mod tests {
use kaspa_consensus_core::tx::{TransactionId, TransactionOutpoint, UtxoEntry};
use kaspa_txscript::{multisig_redeem_script, pay_to_script_hash_script};
// use kaspa_txscript::{multisig_redeem_script, opcodes::codes::OpData65, pay_to_script_hash_script, script_builder::ScriptBuilder};
use rmp_serde::{decode, encode};
use secp256k1::Secp256k1;
use secp256k1::{rand::thread_rng, Keypair};
use std::str::FromStr;
Expand Down Expand Up @@ -279,26 +247,27 @@ mod tests {
}

#[test]
fn test_serialization() {
fn test_pskb_serialization() {
let constructor = mock_pskt_constructor();
let bundle = Bundle::from(constructor.clone());

// Serialize to MessagePack
let mut buf = Vec::new();
encode::write(&mut buf, &bundle).expect("Serialize PSKB");
println!("Serialized: {:?}", buf);
println!("Bundle: {}", serde_json::to_string(&bundle).unwrap());

// Serialize Bundle
let serialized = bundle.serialize().map_err(|err| format!("Unable to serialize bundle: {err}")).unwrap();
println!("Serialized: {}", serialized);

assert!(!bundle.inner_list.is_empty());
assert!(!bundle.0.is_empty());

// todo: discuss why deserializing from MessagePack errors
match decode::from_slice::<Bundle>(&buf) {
match Bundle::deserialize(&serialized) {
Ok(bundle_constructor_deser) => {
println!("Deserialized: {:?}", bundle_constructor_deser);
let pskt_constructor_deser: Option<PSKT<Constructor>> =
bundle_constructor_deser.inner_list.first().map(|inner| PSKT::from(inner.clone()));
bundle_constructor_deser.0.first().map(|inner| PSKT::from(inner.clone()));
match pskt_constructor_deser {
Some(_) => println!("PSKT<Constructor> deserialized successfully"),
None => println!("No elements in inner_list to deserialize"),
None => println!("No elements in the inner list to deserialize"),
}
}
Err(e) => {
Expand All @@ -309,28 +278,28 @@ mod tests {
}

#[test]
fn test_bundle_creation() {
fn test_pskb_bundle_creation() {
let bundle = Bundle::new();
assert!(bundle.inner_list.is_empty());
assert!(bundle.0.is_empty());
}

#[test]
fn test_new_with_pskt() {
fn test_pskb_new_with_pskt() {
let pskt = PSKT::<Creator>::default();
let bundle = Bundle::from(pskt);
assert_eq!(bundle.inner_list.len(), 1);
assert_eq!(bundle.0.len(), 1);
}

#[test]
fn test_add_pskt() {
fn test_pskb_add_pskt() {
let mut bundle = Bundle::new();
let pskt = PSKT::<Creator>::default();
bundle.add_pskt(pskt);
assert_eq!(bundle.inner_list.len(), 1);
assert_eq!(bundle.0.len(), 1);
}

#[test]
fn test_merge_bundles() {
fn test_pskb_merge_bundles() {
let mut bundle1 = Bundle::new();
let mut bundle2 = Bundle::new();

Expand All @@ -342,6 +311,6 @@ mod tests {

bundle1.merge(bundle2);

assert_eq!(bundle1.inner_list.len(), 2);
assert_eq!(bundle1.0.len(), 2);
}
}
4 changes: 4 additions & 0 deletions wallet/pskt/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ pub enum Error {
P2SHExtractError(#[source] TxScriptError),
#[error("PSKB hex serialization error: {0}")]
PskbSerializeToHexError(String),
#[error("PSKB serialization requires 'PSKB' prefix")]
PskbPrefixError,
#[error("PSKT serialization requires 'PSKT' prefix")]
PsktPrefixError,
}
#[derive(thiserror::Error, Debug)]
pub enum ConstructorError {
Expand Down
1 change: 1 addition & 0 deletions wallet/pskt/src/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::{
type Xpub = kaspa_bip32::ExtendedPublicKey<secp256k1::PublicKey>;

#[derive(Debug, Clone, Builder, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[builder(default)]
pub struct Global {
/// The version number of this PSKT.
Expand Down
1 change: 1 addition & 0 deletions wallet/pskt/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::{collections::BTreeMap, marker::PhantomData, ops::Add};

// todo add unknown field? combine them by deduplicating, if there are different values - return error?
#[derive(Builder, Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[builder(default)]
#[builder(setter(skip))]
pub struct Input {
Expand Down
1 change: 1 addition & 0 deletions wallet/pskt/src/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
use std::{collections::BTreeMap, ops::Add};

#[derive(Builder, Default, Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
#[builder(default)]
pub struct Output {
/// The output's amount (serialized as sompi).
Expand Down
Loading

0 comments on commit 78adc19

Please sign in to comment.