Ver Fonte

Remove max_order from keyset database schema

Fixes #1074

Remove the max_order column from the keyset table across the mint database
layer, replacing it with the amounts field that was already being used. This
simplifies the schema by eliminating redundant data.
Cesar Rodas há 2 meses atrás
pai
commit
320c0ee62a

+ 1 - 2
crates/cdk-common/src/database/mint/test/mod.rs

@@ -36,9 +36,8 @@ where
         final_expiry: None,
         derivation_path: DerivationPath::from_str("m/0'/0'/0'").unwrap(),
         derivation_path_index: Some(0),
-        max_order: 32,
         input_fee_ppk: 0,
-        amounts: vec![],
+        amounts: (0..32).map(|n| 2u64.pow(n)).collect(),
     };
     let mut writer = db.begin_transaction().await.expect("db.begin()");
     writer.add_keyset_info(keyset_info).await.unwrap();

+ 0 - 2
crates/cdk-common/src/mint.rs

@@ -589,8 +589,6 @@ pub struct MintKeySetInfo {
     pub derivation_path: DerivationPath,
     /// DerivationPath index of Keyset
     pub derivation_path_index: Option<u32>,
-    /// Max order of keyset
-    pub max_order: u8,
     /// Supported amounts
     pub amounts: Vec<u64>,
     /// Input Fee ppk

+ 3 - 3
crates/cdk-integration-tests/tests/integration_tests_pure.rs

@@ -565,7 +565,7 @@ async fn test_swap_overpay_underpay_fee() {
         .expect("Failed to create test mint");
 
     mint_bob
-        .rotate_keyset(CurrencyUnit::Sat, 32, 1)
+        .rotate_keyset(CurrencyUnit::Sat, (0..32).map(|n| 2u64.pow(n)).collect(), 1)
         .await
         .unwrap();
 
@@ -640,7 +640,7 @@ async fn test_mint_enforce_fee() {
         .expect("Failed to create test mint");
 
     mint_bob
-        .rotate_keyset(CurrencyUnit::Sat, 32, 1)
+        .rotate_keyset(CurrencyUnit::Sat, (0..32).map(|n| 2u64.pow(n)).collect(), 1)
         .await
         .unwrap();
 
@@ -749,7 +749,7 @@ async fn test_mint_change_with_fee_melt() {
         .expect("Failed to create test mint");
 
     mint_bob
-        .rotate_keyset(CurrencyUnit::Sat, 32, 1)
+        .rotate_keyset(CurrencyUnit::Sat, (0..32).map(|n| 2u64.pow(n)).collect(), 1)
         .await
         .unwrap();
 

+ 6 - 2
crates/cdk-integration-tests/tests/mint.rs

@@ -74,7 +74,9 @@ async fn test_correct_keyset() {
         .expect("There is a keyset for unit");
     let old_keyset_info = mint.get_keyset_info(active).expect("There is keyset");
 
-    mint.rotate_keyset(CurrencyUnit::Sat, 32, 0).await.unwrap();
+    mint.rotate_keyset(CurrencyUnit::Sat, (0..32).map(|n| 2u64.pow(n)).collect(), 0)
+        .await
+        .unwrap();
 
     let active = mint.get_active_keysets();
 
@@ -86,7 +88,9 @@ async fn test_correct_keyset() {
 
     assert_ne!(keyset_info.id, old_keyset_info.id);
 
-    mint.rotate_keyset(CurrencyUnit::Sat, 32, 0).await.unwrap();
+    mint.rotate_keyset(CurrencyUnit::Sat, (0..32).map(|n| 2u64.pow(n)).collect(), 0)
+        .await
+        .unwrap();
 
     let active = mint.get_active_keysets();
 

+ 2 - 2
crates/cdk-integration-tests/tests/test_swap_flow.rs

@@ -657,7 +657,7 @@ async fn test_swap_with_fees() {
         .expect("Failed to create test wallet");
 
     // Rotate to keyset with 1 sat per proof fee
-    mint.rotate_keyset(CurrencyUnit::Sat, 32, 1)
+    mint.rotate_keyset(CurrencyUnit::Sat, (0..32).map(|n| 2u64.pow(n)).collect(), 1)
         .await
         .expect("Failed to rotate keyset");
 
@@ -973,7 +973,7 @@ async fn test_wallet_multi_keyset_counter_updates() {
     let first_keyset_id = get_keyset_id(&mint).await;
 
     // Rotate to a second keyset
-    mint.rotate_keyset(CurrencyUnit::Sat, 32, 0)
+    mint.rotate_keyset(CurrencyUnit::Sat, (0..32).map(|n| 2u64.pow(n)).collect(), 0)
         .await
         .expect("Failed to rotate keyset");
 

+ 14 - 5
crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/rotate_next_keyset.rs

@@ -16,9 +16,9 @@ pub struct RotateNextKeysetCommand {
     #[arg(short, long)]
     #[arg(default_value = "sat")]
     unit: String,
-    /// The maximum order (power of 2) for tokens that can be minted with this keyset
+    /// The amounts that can be minted with this keyset (e.g., "1,2,4,8,16")
     #[arg(short, long)]
-    max_order: Option<u8>,
+    amounts: Option<String>,
     /// The input fee in parts per thousand to apply when minting with this keyset
     #[arg(short, long)]
     input_fee_ppk: Option<u64>,
@@ -36,10 +36,19 @@ pub async fn rotate_next_keyset(
     client: &mut CdkMintClient<Channel>,
     sub_command_args: &RotateNextKeysetCommand,
 ) -> Result<()> {
+    let amounts = if let Some(amounts_str) = &sub_command_args.amounts {
+        amounts_str
+            .split(',')
+            .map(|s| s.trim().parse::<u64>())
+            .collect::<Result<Vec<u64>, _>>()?
+    } else {
+        vec![]
+    };
+
     let response = client
         .rotate_next_keyset(Request::new(RotateNextKeysetRequest {
             unit: sub_command_args.unit.clone(),
-            max_order: sub_command_args.max_order.map(|m| m.into()),
+            amounts,
             input_fee_ppk: sub_command_args.input_fee_ppk,
         }))
         .await?;
@@ -47,8 +56,8 @@ pub async fn rotate_next_keyset(
     let response = response.into_inner();
 
     println!(
-        "Rotated to new keyset {} for unit {} with a max order of {} and fee of {}",
-        response.id, response.unit, response.max_order, response.input_fee_ppk
+        "Rotated to new keyset {} for unit {} with amounts {:?} and fee of {}",
+        response.id, response.unit, response.amounts, response.input_fee_ppk
     );
 
     Ok(())

+ 2 - 2
crates/cdk-mint-rpc/src/proto/cdk-mint-rpc.proto

@@ -122,7 +122,7 @@ message UpdateNut04QuoteRequest {
 
 message RotateNextKeysetRequest {
     string unit = 1;
-    optional uint32 max_order = 2;
+    repeated uint64 amounts = 2;
     optional uint64 input_fee_ppk = 3;
 }
 
@@ -130,6 +130,6 @@ message RotateNextKeysetRequest {
 message RotateNextKeysetResponse {
     string id = 1;
     string unit = 2;
-    uint32 max_order = 3;
+    repeated uint64 amounts = 3;
     uint64 input_fee_ppk = 4;
 }

+ 8 - 6
crates/cdk-mint-rpc/src/proto/server.rs

@@ -730,20 +730,22 @@ impl CdkMint for MintRPCServer {
         let unit = CurrencyUnit::from_str(&request.unit)
             .map_err(|_| Status::invalid_argument("Invalid unit".to_string()))?;
 
+        let amounts = if request.amounts.is_empty() {
+            (0..32).map(|n| 2u64.pow(n)).collect()
+        } else {
+            request.amounts
+        };
+
         let keyset_info = self
             .mint
-            .rotate_keyset(
-                unit,
-                request.max_order.map(|a| a as u8).unwrap_or(32),
-                request.input_fee_ppk.unwrap_or(0),
-            )
+            .rotate_keyset(unit, amounts, request.input_fee_ppk.unwrap_or(0))
             .await
             .map_err(|_| Status::invalid_argument("Could not rotate keyset".to_string()))?;
 
         Ok(Response::new(RotateNextKeysetResponse {
             id: keyset_info.id.to_string(),
             unit: keyset_info.unit.to_string(),
-            max_order: keyset_info.max_order.into(),
+            amounts: keyset_info.amounts,
             input_fee_ppk: keyset_info.input_fee_ppk,
         }))
     }

+ 0 - 1
crates/cdk-signatory/src/common.rs

@@ -151,7 +151,6 @@ pub fn create_new_keyset<C: secp256k1::Signing>(
         final_expiry: keyset.final_expiry,
         derivation_path,
         derivation_path_index,
-        max_order: 0,
         amounts: amounts.to_owned(),
         input_fee_ppk,
     };

+ 0 - 1
crates/cdk-signatory/src/signatory.rs

@@ -111,7 +111,6 @@ impl From<SignatoryKeySet> for MintKeySetInfo {
             input_fee_ppk: val.input_fee_ppk,
             derivation_path: Default::default(),
             derivation_path_index: Default::default(),
-            max_order: 0,
             amounts: val.amounts,
             final_expiry: val.final_expiry,
             valid_from: 0,

+ 2 - 0
crates/cdk-sql-common/src/mint/auth/migrations/postgres/20251122000000_drop_max_order.sql

@@ -0,0 +1,2 @@
+-- Drop max_order column from keyset table
+ALTER TABLE keyset DROP COLUMN IF EXISTS max_order;

+ 28 - 0
crates/cdk-sql-common/src/mint/auth/migrations/sqlite/20251122000000_drop_max_order.sql

@@ -0,0 +1,28 @@
+-- Drop max_order column from keyset table
+-- SQLite doesn't support DROP COLUMN directly, so we need to recreate the table
+
+-- Create new table without max_order
+CREATE TABLE keyset_new (
+    id TEXT PRIMARY KEY,
+    unit TEXT NOT NULL,
+    active BOOL NOT NULL,
+    valid_from INTEGER NOT NULL,
+    valid_to INTEGER,
+    derivation_path TEXT NOT NULL,
+    derivation_path_index INTEGER NOT NULL
+);
+
+-- Copy data from old table to new table
+INSERT INTO keyset_new (id, unit, active, valid_from, valid_to, derivation_path, derivation_path_index)
+SELECT id, unit, active, valid_from, valid_to, derivation_path, derivation_path_index
+FROM keyset;
+
+-- Drop old table
+DROP TABLE keyset;
+
+-- Rename new table to original name
+ALTER TABLE keyset_new RENAME TO keyset;
+
+-- Recreate indexes
+CREATE INDEX IF NOT EXISTS unit_index ON keyset(unit);
+CREATE INDEX IF NOT EXISTS active_index ON keyset(active);

+ 6 - 6
crates/cdk-sql-common/src/mint/auth/mod.rs

@@ -88,11 +88,11 @@ where
         INSERT INTO
             keyset (
                 id, unit, active, valid_from, valid_to, derivation_path,
-                max_order, derivation_path_index
+                amounts, input_fee_ppk, derivation_path_index
             )
         VALUES (
             :id, :unit, :active, :valid_from, :valid_to, :derivation_path,
-            :max_order, :derivation_path_index
+            :amounts, :input_fee_ppk, :derivation_path_index
         )
         ON CONFLICT(id) DO UPDATE SET
             unit = excluded.unit,
@@ -100,7 +100,8 @@ where
             valid_from = excluded.valid_from,
             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
         "#,
         )?
@@ -110,7 +111,8 @@ where
         .bind("valid_from", keyset.valid_from as i64)
         .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)
         .await?;
@@ -286,7 +288,6 @@ where
                 valid_to,
                 derivation_path,
                 derivation_path_index,
-                max_order,
                 amounts,
                 input_fee_ppk
             FROM
@@ -311,7 +312,6 @@ where
                 valid_to,
                 derivation_path,
                 derivation_path_index,
-                max_order,
                 amounts,
                 input_fee_ppk
             FROM

+ 2 - 0
crates/cdk-sql-common/src/mint/migrations/postgres/20251122000000_drop_max_order.sql

@@ -0,0 +1,2 @@
+-- Drop max_order column from keyset table
+ALTER TABLE keyset DROP COLUMN IF EXISTS max_order;

+ 30 - 0
crates/cdk-sql-common/src/mint/migrations/sqlite/20251122000000_drop_max_order.sql

@@ -0,0 +1,30 @@
+-- Drop max_order column from keyset table
+-- SQLite doesn't support DROP COLUMN directly, so we need to recreate the table
+
+-- Create new table without max_order
+CREATE TABLE keyset_new (
+    id TEXT PRIMARY KEY,
+    unit TEXT NOT NULL,
+    active BOOL NOT NULL,
+    valid_from INTEGER NOT NULL,
+    valid_to INTEGER,
+    derivation_path TEXT NOT NULL,
+    input_fee_ppk INTEGER,
+    derivation_path_index INTEGER,
+    amounts TEXT
+);
+
+-- Copy data from old table to new table
+INSERT INTO keyset_new (id, unit, active, valid_from, valid_to, derivation_path, input_fee_ppk, derivation_path_index, amounts)
+SELECT id, unit, active, valid_from, valid_to, derivation_path, input_fee_ppk, derivation_path_index, amounts
+FROM keyset;
+
+-- Drop old table
+DROP TABLE keyset;
+
+-- Rename new table to original name
+ALTER TABLE keyset_new RENAME TO keyset;
+
+-- Recreate indexes
+CREATE INDEX IF NOT EXISTS unit_index ON keyset(unit);
+CREATE INDEX IF NOT EXISTS active_index ON keyset(active);

+ 9 - 82
crates/cdk-sql-common/src/mint/mod.rs

@@ -591,11 +591,11 @@ where
         INSERT INTO
             keyset (
                 id, unit, active, valid_from, valid_to, derivation_path,
-                max_order, amounts, input_fee_ppk, derivation_path_index
+                amounts, input_fee_ppk, derivation_path_index
             )
         VALUES (
             :id, :unit, :active, :valid_from, :valid_to, :derivation_path,
-            :max_order, :amounts, :input_fee_ppk, :derivation_path_index
+            :amounts, :input_fee_ppk, :derivation_path_index
         )
         ON CONFLICT(id) DO UPDATE SET
             unit = excluded.unit,
@@ -603,7 +603,6 @@ where
             valid_from = excluded.valid_from,
             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
@@ -615,7 +614,6 @@ where
         .bind("valid_from", keyset.valid_from as i64)
         .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)
@@ -707,7 +705,6 @@ where
                 valid_to,
                 derivation_path,
                 derivation_path_index,
-                max_order,
                 amounts,
                 input_fee_ppk
             FROM
@@ -732,7 +729,6 @@ where
                 valid_to,
                 derivation_path,
                 derivation_path_index,
-                max_order,
                 amounts,
                 input_fee_ppk
             FROM
@@ -2316,16 +2312,14 @@ fn sql_row_to_keyset_info(row: Vec<Column>) -> Result<MintKeySetInfo, Error> {
             valid_to,
             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_or_else(|| Error::Database("amounts field is required".to_string().into()))?;
 
     Ok(MintKeySetInfo {
         id: column_as_string!(id, Id::from_str, Id::from_bytes),
@@ -2334,7 +2328,6 @@ fn sql_row_to_keyset_info(row: Vec<Column>) -> Result<MintKeySetInfo, Error> {
         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,
         amounts,
         input_fee_ppk: column_as_number!(row_keyset_ppk),
         final_expiry: column_as_nullable_number!(valid_to),
@@ -2629,77 +2622,13 @@ fn sql_row_to_saga(row: Vec<Column>) -> Result<mint::Saga, Error> {
 mod test {
     use super::*;
 
-    mod max_order_to_amounts_migrations {
+    mod keyset_amounts_tests {
         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());
-
+        fn keyset_with_amounts() {
             let amounts = (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>();
-            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::<Vec<_>>();
-            let migrated = sql_row_to_keyset_info(vec![
+            let result = sql_row_to_keyset_info(vec![
                 Column::Text("0083a60439303340".to_owned()),
                 Column::Text("sat".to_owned()),
                 Column::Integer(1),
@@ -2707,14 +2636,12 @@ mod test {
                 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);
+            assert!(result.is_ok());
+            let keyset = result.unwrap();
+            assert_eq!(keyset.amounts.len(), 32);
         }
     }
 }

+ 1 - 1
crates/cdk/src/mint/builder.rs

@@ -302,7 +302,7 @@ impl MintBuilder {
     ///
     /// The unit **MUST** already have been added with a ln backend
     pub fn set_unit_fee(&mut self, unit: &CurrencyUnit, input_fee_ppk: u64) -> Result<(), Error> {
-        let (input_fee, _max_order) = self
+        let (input_fee, _) = self
             .supported_units
             .get_mut(unit)
             .ok_or(Error::UnsupportedUnit)?;

+ 2 - 2
crates/cdk/src/mint/keysets/mod.rs

@@ -75,14 +75,14 @@ impl Mint {
     pub async fn rotate_keyset(
         &self,
         unit: CurrencyUnit,
-        max_order: u8,
+        amounts: Vec<u64>,
         input_fee_ppk: u64,
     ) -> Result<MintKeySetInfo, Error> {
         let result = self
             .signatory
             .rotate_keyset(RotateKeyArguments {
                 unit,
-                amounts: (0..max_order).map(|n| 2u64.pow(n.into())).collect(),
+                amounts,
                 input_fee_ppk,
             })
             .await?;

+ 1 - 1
crates/cdk/src/mint/mod.rs

@@ -1071,7 +1071,7 @@ mod tests {
         let first_keyset_id = keysets.keysets[0].id;
 
         // set the first keyset to inactive and generate a new keyset
-        mint.rotate_keyset(CurrencyUnit::default(), 1, 1)
+        mint.rotate_keyset(CurrencyUnit::default(), vec![1], 1)
             .await
             .expect("test");