From ded165f40558618fdb72b6816be5ec6084efbc7b Mon Sep 17 00:00:00 2001 From: C Date: Fri, 5 Sep 2025 19:07:04 -0300 Subject: [PATCH] Update the signatory.proto file to match NUT-XXX (#1032) * Update the signatory.proto file to match NUT-XXX Source: https://github.com/cashubtc/nuts/pull/250/files * Add unit tests as requested in https://github.com/cashubtc/cdk/pull/1032#discussion_r2321436860 * Remove unused types from proto file --- crates/cashu/src/nuts/nut02.rs | 15 ++- crates/cashu/src/quote_id.rs | 2 +- crates/cdk-common/src/database/mint/test.rs | 1 + crates/cdk-common/src/mint.rs | 2 + crates/cdk-signatory/src/common.rs | 11 +- crates/cdk-signatory/src/db_signatory.rs | 14 ++- crates/cdk-signatory/src/proto/convert.rs | 40 +++---- .../cdk-signatory/src/proto/signatory.proto | 21 +++- crates/cdk-signatory/src/signatory.rs | 3 +- crates/cdk-sql-common/src/mint/auth/mod.rs | 2 + crates/cdk-sql-common/src/mint/migrations.rs | 2 + .../20250903200000_add_signatory_amounts.sql | 1 + .../20250903200000_add_signatory_amounts.sql | 33 ++++++ crates/cdk-sql-common/src/mint/mod.rs | 111 +++++++++++++++++- crates/cdk/src/mint/keysets/mod.rs | 2 +- 15 files changed, 207 insertions(+), 53 deletions(-) create mode 100644 crates/cdk-sql-common/src/mint/migrations/postgres/20250903200000_add_signatory_amounts.sql create mode 100644 crates/cdk-sql-common/src/mint/migrations/sqlite/20250903200000_add_signatory_amounts.sql diff --git a/crates/cashu/src/nuts/nut02.rs b/crates/cashu/src/nuts/nut02.rs index 4baceaf4..7835de87 100644 --- a/crates/cashu/src/nuts/nut02.rs +++ b/crates/cashu/src/nuts/nut02.rs @@ -553,13 +553,12 @@ impl MintKeySet { secp: &Secp256k1, xpriv: Xpriv, unit: CurrencyUnit, - max_order: u8, + amounts: &[u64], final_expiry: Option, version: KeySetVersion, ) -> Self { let mut map = BTreeMap::new(); - for i in 0..max_order { - let amount = Amount::from(2_u64.pow(i as u32)); + for (i, amount) in amounts.iter().enumerate() { let secret_key = xpriv .derive_priv( secp, @@ -569,7 +568,7 @@ impl MintKeySet { .private_key; let public_key = secret_key.public_key(secp); map.insert( - amount, + amount.into(), MintKeyPair { secret_key: secret_key.into(), public_key: public_key.into(), @@ -594,7 +593,7 @@ impl MintKeySet { pub fn generate_from_seed( secp: &Secp256k1, seed: &[u8], - max_order: u8, + amounts: &[u64], currency_unit: CurrencyUnit, derivation_path: DerivationPath, final_expiry: Option, @@ -607,7 +606,7 @@ impl MintKeySet { .derive_priv(secp, &derivation_path) .expect("RNG busted"), currency_unit, - max_order, + amounts, final_expiry, version, ) @@ -617,7 +616,7 @@ impl MintKeySet { pub fn generate_from_xpriv( secp: &Secp256k1, xpriv: Xpriv, - max_order: u8, + amounts: &[u64], currency_unit: CurrencyUnit, derivation_path: DerivationPath, final_expiry: Option, @@ -629,7 +628,7 @@ impl MintKeySet { .derive_priv(secp, &derivation_path) .expect("RNG busted"), currency_unit, - max_order, + amounts, final_expiry, version, ) diff --git a/crates/cashu/src/quote_id.rs b/crates/cashu/src/quote_id.rs index 58ccac59..4b14e21f 100644 --- a/crates/cashu/src/quote_id.rs +++ b/crates/cashu/src/quote_id.rs @@ -48,7 +48,7 @@ impl From for QuoteId { impl fmt::Display for QuoteId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - QuoteId::BASE64(s) => write!(f, "{}", s), + QuoteId::BASE64(s) => write!(f, "{s}"), QuoteId::UUID(u) => write!(f, "{}", u.hyphenated()), } } diff --git a/crates/cdk-common/src/database/mint/test.rs b/crates/cdk-common/src/database/mint/test.rs index de401104..2d681a6b 100644 --- a/crates/cdk-common/src/database/mint/test.rs +++ b/crates/cdk-common/src/database/mint/test.rs @@ -29,6 +29,7 @@ where derivation_path_index: Some(0), max_order: 32, input_fee_ppk: 0, + amounts: vec![], }; let mut writer = db.begin_transaction().await.expect("db.begin()"); writer.add_keyset_info(keyset_info).await.unwrap(); diff --git a/crates/cdk-common/src/mint.rs b/crates/cdk-common/src/mint.rs index 9c63b0fd..a6d3d91e 100644 --- a/crates/cdk-common/src/mint.rs +++ b/crates/cdk-common/src/mint.rs @@ -313,6 +313,8 @@ pub struct MintKeySetInfo { pub derivation_path_index: Option, /// Max order of keyset pub max_order: u8, + /// Supported amounts + pub amounts: Vec, /// Input Fee ppk #[serde(default = "default_fee")] pub input_fee_ppk: u64, diff --git a/crates/cdk-signatory/src/common.rs b/crates/cdk-signatory/src/common.rs index 6663f881..d7d4d344 100644 --- a/crates/cdk-signatory/src/common.rs +++ b/crates/cdk-signatory/src/common.rs @@ -66,7 +66,7 @@ pub async fn init_keysets( let keyset = MintKeySet::generate_from_xpriv( secp_ctx, xpriv, - highest_index_keyset.max_order, + &highest_index_keyset.amounts, highest_index_keyset.unit.clone(), highest_index_keyset.derivation_path.clone(), highest_index_keyset.final_expiry, @@ -98,7 +98,7 @@ pub async fn init_keysets( derivation_path, Some(derivation_path_index), unit.clone(), - *max_order, + &highest_index_keyset.amounts, *input_fee_ppk, // TODO: add Mint settings for a final expiry of newly generated keysets None, @@ -128,7 +128,7 @@ pub fn create_new_keyset( derivation_path: DerivationPath, derivation_path_index: Option, unit: CurrencyUnit, - max_order: u8, + amounts: &[u64], input_fee_ppk: u64, final_expiry: Option, ) -> (MintKeySet, MintKeySetInfo) { @@ -138,7 +138,7 @@ pub fn create_new_keyset( .derive_priv(secp, &derivation_path) .expect("RNG busted"), unit, - max_order, + amounts, final_expiry, // TODO: change this to Version01 to generate keysets v2 cdk_common::nut02::KeySetVersion::Version00, @@ -151,7 +151,8 @@ pub fn create_new_keyset( final_expiry: keyset.final_expiry, derivation_path, derivation_path_index, - max_order, + max_order: 0, + amounts: amounts.to_owned(), input_fee_ppk, }; (keyset, keyset_info) diff --git a/crates/cdk-signatory/src/db_signatory.rs b/crates/cdk-signatory/src/db_signatory.rs index c1dae7f8..d446ba2b 100644 --- a/crates/cdk-signatory/src/db_signatory.rs +++ b/crates/cdk-signatory/src/db_signatory.rs @@ -65,13 +65,17 @@ impl DbSignatory { } }; + let amounts = (0..max_order) + .map(|i| 2_u64.pow(i as u32)) + .collect::>(); + let (keyset, keyset_info) = create_new_keyset( &secp_ctx, xpriv, derivation_path, Some(0), unit.clone(), - max_order, + &amounts, fee, // TODO: add and connect settings for this None, @@ -132,7 +136,7 @@ impl DbSignatory { MintKeySet::generate_from_xpriv( &self.secp_ctx, self.xpriv, - keyset_info.max_order, + &keyset_info.amounts, keyset_info.unit.clone(), keyset_info.derivation_path.clone(), keyset_info.final_expiry, @@ -241,7 +245,7 @@ impl Signatory for DbSignatory { derivation_path, Some(path_index), args.unit.clone(), - args.max_order, + &args.amounts, args.input_fee_ppk, // TODO: add and connect settings for this None, @@ -274,7 +278,7 @@ mod test { let keyset = MintKeySet::generate_from_seed( &Secp256k1::new(), seed, - 2, + &[1, 2], CurrencyUnit::Sat, derivation_path_from_unit(CurrencyUnit::Sat, 0).unwrap(), None, @@ -320,7 +324,7 @@ mod test { let keyset = MintKeySet::generate_from_xpriv( &Secp256k1::new(), xpriv, - 2, + &[1, 2], CurrencyUnit::Sat, derivation_path_from_unit(CurrencyUnit::Sat, 0).unwrap(), None, diff --git a/crates/cdk-signatory/src/proto/convert.rs b/crates/cdk-signatory/src/proto/convert.rs index 0bb11c03..91692cd0 100644 --- a/crates/cdk-signatory/src/proto/convert.rs +++ b/crates/cdk-signatory/src/proto/convert.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use cdk_common::secret::Secret; use cdk_common::util::hex; -use cdk_common::{Amount, PublicKey}; +use cdk_common::{Amount, Id, PublicKey}; use tonic::Status; use super::*; @@ -44,7 +44,7 @@ impl TryInto for KeySet { fn try_into(self) -> Result { Ok(crate::signatory::SignatoryKeySet { - id: self.id.parse()?, + id: Id::from_bytes(&self.id)?, unit: self .unit .ok_or(cdk_common::Error::Custom(INTERNAL_ERROR.to_owned()))? @@ -68,7 +68,7 @@ impl TryInto for KeySet { impl From for KeySet { fn from(keyset: crate::signatory::SignatoryKeySet) -> Self { Self { - id: keyset.id.to_string(), + id: keyset.id.to_bytes(), unit: Some(keyset.unit.into()), active: keyset.active, input_fee_ppk: keyset.input_fee_ppk, @@ -80,6 +80,7 @@ impl From for KeySet { .collect(), }), final_expiry: keyset.final_expiry, + version: Default::default(), } } } @@ -141,7 +142,7 @@ impl From for BlindSignature { BlindSignature { amount: value.amount.into(), blinded_secret: value.c.to_bytes().to_vec(), - keyset_id: value.keyset_id.to_string(), + keyset_id: value.keyset_id.to_bytes(), dleq: value.dleq.map(|x| x.into()), } } @@ -161,7 +162,7 @@ impl From for Proof { fn from(value: cdk_common::Proof) -> Self { Proof { amount: value.amount.into(), - keyset_id: value.keyset_id.to_string(), + keyset_id: value.keyset_id.to_bytes(), secret: value.secret.to_bytes(), c: value.c.to_bytes().to_vec(), } @@ -179,9 +180,7 @@ impl TryInto for Proof { Ok(cdk_common::Proof { amount: self.amount.into(), - keyset_id: self - .keyset_id - .parse() + keyset_id: Id::from_bytes(&self.keyset_id) .map_err(|e| Status::from_error(Box::new(e)))?, secret: Secret::new(secret), c: cdk_common::PublicKey::from_slice(&self.c) @@ -199,7 +198,7 @@ impl TryInto for BlindSignature { Ok(cdk_common::BlindSignature { amount: self.amount.into(), c: cdk_common::PublicKey::from_slice(&self.blinded_secret)?, - keyset_id: self.keyset_id.parse().expect("Invalid keyset id"), + keyset_id: Id::from_bytes(&self.keyset_id)?, dleq: self.dleq.map(|dleq| dleq.try_into()).transpose()?, }) } @@ -209,7 +208,7 @@ impl From for BlindedMessage { fn from(value: cdk_common::BlindedMessage) -> Self { BlindedMessage { amount: value.amount.into(), - keyset_id: value.keyset_id.to_string(), + keyset_id: value.keyset_id.to_bytes(), blinded_secret: value.blinded_secret.to_bytes().to_vec(), } } @@ -220,9 +219,7 @@ impl TryInto for BlindedMessage { fn try_into(self) -> Result { Ok(cdk_common::BlindedMessage { amount: self.amount.into(), - keyset_id: self - .keyset_id - .parse() + keyset_id: Id::from_bytes(&self.keyset_id) .map_err(|e| Status::from_error(Box::new(e)))?, blinded_secret: cdk_common::PublicKey::from_slice(&self.blinded_secret) .map_err(|e| Status::from_error(Box::new(e)))?, @@ -311,10 +308,7 @@ impl TryInto for KeySet { type Error = cdk_common::error::Error; fn try_into(self) -> Result { Ok(cdk_common::KeySet { - id: self - .id - .parse() - .map_err(|_| cdk_common::error::Error::Custom("Invalid ID".to_owned()))?, + id: Id::from_bytes(&self.id)?, unit: self .unit .ok_or(cdk_common::error::Error::Custom(INTERNAL_ERROR.to_owned()))? @@ -337,7 +331,7 @@ impl From for RotationRequest { fn from(value: crate::signatory::RotateKeyArguments) -> Self { Self { unit: Some(value.unit.into()), - max_order: value.max_order.into(), + amounts: value.amounts, input_fee_ppk: value.input_fee_ppk, } } @@ -352,10 +346,7 @@ impl TryInto for RotationRequest { .unit .ok_or(Status::invalid_argument("unit not set"))? .try_into()?, - max_order: self - .max_order - .try_into() - .map_err(|_| Status::invalid_argument("Invalid max_order"))?, + amounts: self.amounts, input_fee_ppk: self.input_fee_ppk, }) } @@ -364,12 +355,13 @@ impl TryInto for RotationRequest { impl From for KeySet { fn from(value: cdk_common::KeySetInfo) -> Self { Self { - id: value.id.into(), + id: value.id.to_bytes(), unit: Some(value.unit.into()), active: value.active, input_fee_ppk: value.input_fee_ppk, keys: Default::default(), final_expiry: value.final_expiry, + version: Default::default(), } } } @@ -379,7 +371,7 @@ impl TryInto for KeySet { fn try_into(self) -> Result { Ok(cdk_common::KeySetInfo { - id: self.id.try_into()?, + id: Id::from_bytes(&self.id)?, unit: self .unit .ok_or(cdk_common::Error::Custom(INTERNAL_ERROR.to_owned()))? diff --git a/crates/cdk-signatory/src/proto/signatory.proto b/crates/cdk-signatory/src/proto/signatory.proto index 8cb91211..600bb223 100644 --- a/crates/cdk-signatory/src/proto/signatory.proto +++ b/crates/cdk-signatory/src/proto/signatory.proto @@ -32,7 +32,7 @@ message BlindedMessages { // Represents a blinded message message BlindedMessage { uint64 amount = 1; - string keyset_id = 2; + bytes keyset_id = 2; bytes blinded_secret = 3; } @@ -57,12 +57,13 @@ message SignatoryKeysets { } message KeySet { - string id = 1; + bytes id = 1; CurrencyUnit unit = 2; bool active = 3; uint64 input_fee_ppk = 4; Keys keys = 5; optional uint64 final_expiry = 6; + uint64 version = 7; } message Keys { @@ -72,7 +73,7 @@ message Keys { message RotationRequest { CurrencyUnit unit = 1; uint64 input_fee_ppk = 2; - uint32 max_order = 3; + repeated uint64 amounts = 3; } enum CurrencyUnitType { @@ -99,18 +100,28 @@ message Proofs { message Proof { uint64 amount = 1; - string keyset_id = 2; + bytes keyset_id = 2; bytes secret = 3; bytes c = 4; } +message ProofDLEQ { + bytes e = 1; + bytes s = 2; + bytes r = 3; +} + +message SigningResponse { + Error error = 1; + BlindSignatures blind_signatures = 2; +} message BlindSignatures { repeated BlindSignature blind_signatures = 1; } message BlindSignature { uint64 amount = 1; - string keyset_id = 2; + bytes keyset_id = 2; bytes blinded_secret = 3; optional BlindSignatureDLEQ dleq = 4; } diff --git a/crates/cdk-signatory/src/signatory.rs b/crates/cdk-signatory/src/signatory.rs index 31ea99c3..1b7e2257 100644 --- a/crates/cdk-signatory/src/signatory.rs +++ b/crates/cdk-signatory/src/signatory.rs @@ -43,7 +43,7 @@ pub struct RotateKeyArguments { /// Unit pub unit: CurrencyUnit, /// Max order - pub max_order: u8, + pub amounts: Vec, /// Input fee pub input_fee_ppk: u64, } @@ -110,6 +110,7 @@ impl From for MintKeySetInfo { derivation_path: Default::default(), derivation_path_index: Default::default(), max_order: 0, + amounts: vec![], final_expiry: val.final_expiry, valid_from: 0, } diff --git a/crates/cdk-sql-common/src/mint/auth/mod.rs b/crates/cdk-sql-common/src/mint/auth/mod.rs index 8effd9fc..771f509f 100644 --- a/crates/cdk-sql-common/src/mint/auth/mod.rs +++ b/crates/cdk-sql-common/src/mint/auth/mod.rs @@ -284,6 +284,7 @@ where derivation_path, derivation_path_index, max_order, + amounts, input_fee_ppk FROM keyset @@ -308,6 +309,7 @@ where derivation_path, derivation_path_index, max_order, + amounts, input_fee_ppk FROM keyset diff --git a/crates/cdk-sql-common/src/mint/migrations.rs b/crates/cdk-sql-common/src/mint/migrations.rs index eb8e89fc..eddf0552 100644 --- a/crates/cdk-sql-common/src/mint/migrations.rs +++ b/crates/cdk-sql-common/src/mint/migrations.rs @@ -29,4 +29,6 @@ pub static MIGRATIONS: &[(&str, &str, &str)] = &[ ("sqlite", "20250819200000_remove_request_lookup_kind_constraints.sql", include_str!(r#"./migrations/sqlite/20250819200000_remove_request_lookup_kind_constraints.sql"#)), ("sqlite", "20250901090000_add_kv_store.sql", include_str!(r#"./migrations/sqlite/20250901090000_add_kv_store.sql"#)), ("postgres", "20250901090000_add_kv_store.sql", include_str!(r#"./migrations/postgres/20250901090000_add_kv_store.sql"#)), + ("sqlite", "20250903200000_add_signatory_amounts.sql", include_str!(r#"./migrations/sqlite/20250903200000_add_signatory_amounts.sql"#)), + ("postgres", "20250903200000_add_signatory_amounts.sql", include_str!(r#"./migrations/postgres/20250903200000_add_signatory_amounts.sql"#)), ]; diff --git a/crates/cdk-sql-common/src/mint/migrations/postgres/20250903200000_add_signatory_amounts.sql b/crates/cdk-sql-common/src/mint/migrations/postgres/20250903200000_add_signatory_amounts.sql new file mode 100644 index 00000000..cef8c730 --- /dev/null +++ b/crates/cdk-sql-common/src/mint/migrations/postgres/20250903200000_add_signatory_amounts.sql @@ -0,0 +1 @@ +ALTER TABLE keyset ADD COLUMN amounts TEXT DEFAULT NULL; diff --git a/crates/cdk-sql-common/src/mint/migrations/sqlite/20250903200000_add_signatory_amounts.sql b/crates/cdk-sql-common/src/mint/migrations/sqlite/20250903200000_add_signatory_amounts.sql new file mode 100644 index 00000000..348591f9 --- /dev/null +++ b/crates/cdk-sql-common/src/mint/migrations/sqlite/20250903200000_add_signatory_amounts.sql @@ -0,0 +1,33 @@ +CREATE TABLE keyset_new ( + id TEXT PRIMARY KEY, + unit TEXT NOT NULL, + active BOOL NOT NULL, + valid_from INTEGER NOT NULL, + valid_to INTEGER, + max_order INTEGER NOT NULL, + amounts TEXT DEFAULT NULL, + input_fee_ppk INTEGER, + derivation_path TEXT NOT NULL, + derivation_path_index INTEGER +); + + +INSERT INTO keyset_new SELECT + id, + unit, + active, + valid_from, + valid_to, + max_order, + NULL, + input_fee_ppk, + derivation_path, + derivation_path_index +FROM keyset; + +DROP TABLE keyset; + +ALTER TABLE keyset_new RENAME TO keyset; + +CREATE INDEX unit_index ON keyset(unit); +CREATE INDEX active_index ON keyset(active); diff --git a/crates/cdk-sql-common/src/mint/mod.rs b/crates/cdk-sql-common/src/mint/mod.rs index 8e1fcdc3..9e83e8c3 100644 --- a/crates/cdk-sql-common/src/mint/mod.rs +++ b/crates/cdk-sql-common/src/mint/mod.rs @@ -383,11 +383,11 @@ where INSERT INTO keyset ( id, unit, active, valid_from, valid_to, derivation_path, - max_order, input_fee_ppk, derivation_path_index + max_order, amounts, input_fee_ppk, derivation_path_index ) VALUES ( :id, :unit, :active, :valid_from, :valid_to, :derivation_path, - :max_order, :input_fee_ppk, :derivation_path_index + :max_order, :amounts, :input_fee_ppk, :derivation_path_index ) ON CONFLICT(id) DO UPDATE SET unit = excluded.unit, @@ -396,6 +396,7 @@ where valid_to = excluded.valid_to, derivation_path = excluded.derivation_path, max_order = excluded.max_order, + amounts = excluded.amounts, input_fee_ppk = excluded.input_fee_ppk, derivation_path_index = excluded.derivation_path_index "#, @@ -407,6 +408,7 @@ where .bind("valid_to", keyset.final_expiry.map(|v| v as i64)) .bind("derivation_path", keyset.derivation_path.to_string()) .bind("max_order", keyset.max_order) + .bind("amounts", serde_json::to_string(&keyset.amounts).ok()) .bind("input_fee_ppk", keyset.input_fee_ppk as i64) .bind("derivation_path_index", keyset.derivation_path_index) .execute(&self.inner) @@ -496,6 +498,7 @@ where derivation_path, derivation_path_index, max_order, + amounts, input_fee_ppk FROM keyset @@ -520,6 +523,7 @@ where derivation_path, derivation_path_index, max_order, + amounts, input_fee_ppk FROM keyset @@ -1837,10 +1841,16 @@ fn sql_row_to_keyset_info(row: Vec) -> Result { derivation_path, derivation_path_index, max_order, + amounts, row_keyset_ppk ) = row ); + let max_order: u8 = column_as_number!(max_order); + let amounts = column_as_nullable_string!(amounts) + .and_then(|str| serde_json::from_str(&str).ok()) + .unwrap_or_else(|| (0..max_order).map(|m| 2u64.pow(m.into())).collect()); + Ok(MintKeySetInfo { id: column_as_string!(id, Id::from_str, Id::from_bytes), unit: column_as_string!(unit, CurrencyUnit::from_str), @@ -1848,7 +1858,8 @@ fn sql_row_to_keyset_info(row: Vec) -> Result { valid_from: column_as_number!(valid_from), derivation_path: column_as_string!(derivation_path, DerivationPath::from_str), derivation_path_index: column_as_nullable_number!(derivation_path_index), - max_order: column_as_number!(max_order), + max_order, + amounts, input_fee_ppk: column_as_number!(row_keyset_ppk), final_expiry: column_as_nullable_number!(valid_to), }) @@ -2062,3 +2073,97 @@ fn sql_row_to_blind_signature(row: Vec) -> Result dleq, }) } + +#[cfg(test)] +mod test { + use super::*; + + mod max_order_to_amounts_migrations { + use super::*; + + #[test] + fn legacy_payload() { + let result = sql_row_to_keyset_info(vec![ + Column::Text("0083a60439303340".to_owned()), + Column::Text("sat".to_owned()), + Column::Integer(1), + Column::Integer(1749844864), + Column::Null, + Column::Text("0'/0'/0'".to_owned()), + Column::Integer(0), + Column::Integer(32), + Column::Null, + Column::Integer(0), + ]); + assert!(result.is_ok()); + } + + #[test] + fn migrated_payload() { + let legacy = sql_row_to_keyset_info(vec![ + Column::Text("0083a60439303340".to_owned()), + Column::Text("sat".to_owned()), + Column::Integer(1), + Column::Integer(1749844864), + Column::Null, + Column::Text("0'/0'/0'".to_owned()), + Column::Integer(0), + Column::Integer(32), + Column::Null, + Column::Integer(0), + ]); + assert!(legacy.is_ok()); + + let amounts = (0..32).map(|x| 2u64.pow(x)).collect::>(); + let migrated = sql_row_to_keyset_info(vec![ + Column::Text("0083a60439303340".to_owned()), + Column::Text("sat".to_owned()), + Column::Integer(1), + Column::Integer(1749844864), + Column::Null, + Column::Text("0'/0'/0'".to_owned()), + Column::Integer(0), + Column::Integer(32), + Column::Text(serde_json::to_string(&amounts).expect("valid json")), + Column::Integer(0), + ]); + assert!(migrated.is_ok()); + assert_eq!(legacy.unwrap(), migrated.unwrap()); + } + + #[test] + fn amounts_over_max_order() { + let legacy = sql_row_to_keyset_info(vec![ + Column::Text("0083a60439303340".to_owned()), + Column::Text("sat".to_owned()), + Column::Integer(1), + Column::Integer(1749844864), + Column::Null, + Column::Text("0'/0'/0'".to_owned()), + Column::Integer(0), + Column::Integer(32), + Column::Null, + Column::Integer(0), + ]); + assert!(legacy.is_ok()); + + let amounts = (0..16).map(|x| 2u64.pow(x)).collect::>(); + let migrated = sql_row_to_keyset_info(vec![ + Column::Text("0083a60439303340".to_owned()), + Column::Text("sat".to_owned()), + Column::Integer(1), + Column::Integer(1749844864), + Column::Null, + Column::Text("0'/0'/0'".to_owned()), + Column::Integer(0), + Column::Integer(32), + Column::Text(serde_json::to_string(&amounts).expect("valid json")), + Column::Integer(0), + ]); + assert!(migrated.is_ok()); + let migrated = migrated.unwrap(); + assert_ne!(legacy.unwrap(), migrated); + assert_eq!(migrated.amounts.len(), 16); + } + } +} diff --git a/crates/cdk/src/mint/keysets/mod.rs b/crates/cdk/src/mint/keysets/mod.rs index c9a06267..bfe8c853 100644 --- a/crates/cdk/src/mint/keysets/mod.rs +++ b/crates/cdk/src/mint/keysets/mod.rs @@ -82,7 +82,7 @@ impl Mint { .signatory .rotate_keyset(RotateKeyArguments { unit, - max_order, + amounts: (0..max_order).map(|n| 2u64.pow(n.into())).collect(), input_fee_ppk, }) .await?;