Przeglądaj źródła

drk: store proper information for token mint authority

skoupidi 2 lat temu
rodzic
commit
32fcb6e6df

+ 3 - 2
bin/drk/money.sql

@@ -38,8 +38,9 @@ CREATE TABLE IF NOT EXISTS BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o_money_co
 
 
 -- Arbitrary tokens
 -- Arbitrary tokens
 CREATE TABLE IF NOT EXISTS BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o_money_tokens (
 CREATE TABLE IF NOT EXISTS BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o_money_tokens (
-	mint_authority BLOB PRIMARY KEY NOT NULL,
-	token_id BLOB NOT NULL,
+	token_id BLOB PRIMARY KEY NOT NULL,
+	mint_authority BLOB NOT NULL,
+	token_blind BLOB NOT NULL,
 	is_frozen INTEGER NOT NULL
 	is_frozen INTEGER NOT NULL
 );
 );
 
 

+ 7 - 1
bin/drk/src/cli_util.rs

@@ -372,7 +372,13 @@ pub fn generate_completions(shell: &str) -> Result<()> {
         .subcommands(vec![add, show, remove]);
         .subcommands(vec![add, show, remove]);
 
 
     // Token
     // Token
-    let import = SubCommand::with_name("import").about("Import a mint authority secret from stdin");
+    let secret_key = Arg::with_name("secret_key").help("Mint authority secret key");
+
+    let token_blind = Arg::with_name("token_blind").help("Mint authority token blind");
+
+    let import = SubCommand::with_name("import")
+        .about("Import a mint authority")
+        .args(&vec![secret_key, token_blind]);
 
 
     let generate_mint =
     let generate_mint =
         SubCommand::with_name("generate-mint").about("Generate a new mint authority");
         SubCommand::with_name("generate-mint").about("Generate a new mint authority");

+ 34 - 28
bin/drk/src/main.rs

@@ -45,7 +45,7 @@ use darkfi::{
 };
 };
 use darkfi_money_contract::model::{Coin, TokenId};
 use darkfi_money_contract::model::{Coin, TokenId};
 use darkfi_sdk::{
 use darkfi_sdk::{
-    crypto::{FuncId, PublicKey, SecretKey},
+    crypto::{BaseBlind, FuncId, PublicKey, SecretKey},
     pasta::{group::ff::PrimeField, pallas},
     pasta::{group::ff::PrimeField, pallas},
     tx::TransactionHash,
     tx::TransactionHash,
 };
 };
@@ -455,8 +455,14 @@ enum AliasSubcmd {
 
 
 #[derive(Clone, Debug, Deserialize, StructOpt)]
 #[derive(Clone, Debug, Deserialize, StructOpt)]
 enum TokenSubcmd {
 enum TokenSubcmd {
-    /// Import a mint authority secret from stdin
-    Import,
+    /// Import a mint authority
+    Import {
+        /// Mint authority secret key
+        secret_key: String,
+
+        /// Mint authority token blind
+        token_blind: String,
+    },
 
 
     /// Generate a new mint authority
     /// Generate a new mint authority
     GenerateMint,
     GenerateMint,
@@ -1531,41 +1537,35 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
         },
         },
 
 
         Subcmd::Token { command } => match command {
         Subcmd::Token { command } => match command {
-            TokenSubcmd::Import => {
-                let mut buf = String::new();
-                stdin().read_to_string(&mut buf)?;
-                let mint_authority = match SecretKey::from_str(buf.trim()) {
-                    Ok(ma) => ma,
+            TokenSubcmd::Import { secret_key, token_blind } => {
+                let mint_authority = match SecretKey::from_str(&secret_key) {
+                    Ok(r) => r,
                     Err(e) => {
                     Err(e) => {
                         eprintln!("Invalid secret key: {e:?}");
                         eprintln!("Invalid secret key: {e:?}");
                         exit(2);
                         exit(2);
                     }
                     }
                 };
                 };
 
 
-                let drk = Drk::new(args.wallet_path, args.wallet_pass, None, ex).await?;
-                if let Err(e) = drk.import_mint_authority(mint_authority).await {
-                    eprintln!("Importing mint authority failed: {e:?}");
-                    exit(2);
+                let token_blind = match BaseBlind::from_str(&token_blind) {
+                    Ok(r) => r,
+                    Err(e) => {
+                        eprintln!("Invalid recipient: {e:?}");
+                        exit(2);
+                    }
                 };
                 };
 
 
-                let token_id = TokenId::derive(mint_authority);
+                let drk = Drk::new(args.wallet_path, args.wallet_pass, None, ex).await?;
+                let token_id = drk.import_mint_authority(mint_authority, token_blind).await?;
                 println!("Successfully imported mint authority for token ID: {token_id}");
                 println!("Successfully imported mint authority for token ID: {token_id}");
 
 
                 Ok(())
                 Ok(())
             }
             }
 
 
             TokenSubcmd::GenerateMint => {
             TokenSubcmd::GenerateMint => {
-                let mint_authority = SecretKey::random(&mut OsRng);
-
                 let drk = Drk::new(args.wallet_path, args.wallet_pass, None, ex).await?;
                 let drk = Drk::new(args.wallet_path, args.wallet_pass, None, ex).await?;
-
-                if let Err(e) = drk.import_mint_authority(mint_authority).await {
-                    eprintln!("Importing mint authority failed: {e:?}");
-                    exit(2);
-                };
-
-                // TODO: see TokenAttributes struct. I'm not sure how to restructure this rn.
-                let token_id = TokenId::derive(mint_authority);
+                let mint_authority = SecretKey::random(&mut OsRng);
+                let token_blind = BaseBlind::random(&mut OsRng);
+                let token_id = drk.import_mint_authority(mint_authority, token_blind).await?;
                 println!("Successfully imported mint authority for token ID: {token_id}");
                 println!("Successfully imported mint authority for token ID: {token_id}");
 
 
                 Ok(())
                 Ok(())
@@ -1573,7 +1573,7 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
 
 
             TokenSubcmd::List => {
             TokenSubcmd::List => {
                 let drk = Drk::new(args.wallet_path, args.wallet_pass, None, ex).await?;
                 let drk = Drk::new(args.wallet_path, args.wallet_pass, None, ex).await?;
-                let tokens = drk.list_tokens().await?;
+                let tokens = drk.get_mint_authorities().await?;
                 let aliases_map = match drk.get_aliases_mapped_by_token().await {
                 let aliases_map = match drk.get_aliases_mapped_by_token().await {
                     Ok(map) => map,
                     Ok(map) => map,
                     Err(e) => {
                     Err(e) => {
@@ -1584,15 +1584,21 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
 
 
                 let mut table = Table::new();
                 let mut table = Table::new();
                 table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
                 table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-                table.set_titles(row!["Token ID", "Aliases", "Mint Authority", "Frozen"]);
+                table.set_titles(row![
+                    "Token ID",
+                    "Aliases",
+                    "Mint Authority",
+                    "Token Blind",
+                    "Frozen"
+                ]);
 
 
-                for (token_id, authority, frozen) in tokens {
+                for (token_id, authority, blind, frozen) in tokens {
                     let aliases = match aliases_map.get(&token_id.to_string()) {
                     let aliases = match aliases_map.get(&token_id.to_string()) {
                         Some(a) => a,
                         Some(a) => a,
                         None => "-",
                         None => "-",
                     };
                     };
 
 
-                    table.add_row(row![token_id, aliases, authority, frozen]);
+                    table.add_row(row![token_id, aliases, authority, blind, frozen]);
                 }
                 }
 
 
                 if table.is_empty() {
                 if table.is_empty() {
@@ -1695,7 +1701,7 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
                 }
                 }
 
 
                 if table.is_empty() {
                 if table.is_empty() {
-                    eprintln!("No deploy authorities found");
+                    println!("No deploy authorities found");
                 } else {
                 } else {
                     println!("{table}");
                     println!("{table}");
                 }
                 }

+ 2 - 1
bin/drk/src/money.rs

@@ -103,8 +103,9 @@ pub const MONEY_COINS_COL_LEAF_POSITION: &str = "leaf_position";
 pub const MONEY_COINS_COL_MEMO: &str = "memo";
 pub const MONEY_COINS_COL_MEMO: &str = "memo";
 
 
 // MONEY_TOKENS_TABLE
 // MONEY_TOKENS_TABLE
-pub const MONEY_TOKENS_COL_MINT_AUTHORITY: &str = "mint_authority";
 pub const MONEY_TOKENS_COL_TOKEN_ID: &str = "token_id";
 pub const MONEY_TOKENS_COL_TOKEN_ID: &str = "token_id";
+pub const MONEY_TOKENS_COL_MINT_AUTHORITY: &str = "mint_authority";
+pub const MONEY_TOKENS_COL_TOKEN_BLIND: &str = "token_blind";
 pub const MONEY_TOKENS_COL_IS_FROZEN: &str = "is_frozen";
 pub const MONEY_TOKENS_COL_IS_FROZEN: &str = "is_frozen";
 
 
 // MONEY_ALIASES_TABLE
 // MONEY_ALIASES_TABLE

+ 74 - 25
bin/drk/src/token.rs

@@ -37,7 +37,8 @@ use darkfi_money_contract::{
 };
 };
 use darkfi_sdk::{
 use darkfi_sdk::{
     crypto::{
     crypto::{
-        contract_id::MONEY_CONTRACT_ID, Blind, FuncId, FuncRef, Keypair, PublicKey, SecretKey,
+        contract_id::MONEY_CONTRACT_ID, poseidon_hash, BaseBlind, Blind, FuncId, FuncRef, Keypair,
+        PublicKey, SecretKey,
     },
     },
     dark_tree::DarkLeaf,
     dark_tree::DarkLeaf,
     pasta::pallas,
     pasta::pallas,
@@ -46,70 +47,118 @@ use darkfi_sdk::{
 use darkfi_serial::{deserialize_async, serialize_async, AsyncEncodable};
 use darkfi_serial::{deserialize_async, serialize_async, AsyncEncodable};
 
 
 use crate::{
 use crate::{
-    error::WalletDbResult,
     money::{
     money::{
         BALANCE_BASE10_DECIMALS, MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_MINT_AUTHORITY,
         BALANCE_BASE10_DECIMALS, MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_MINT_AUTHORITY,
-        MONEY_TOKENS_COL_TOKEN_ID, MONEY_TOKENS_TABLE,
+        MONEY_TOKENS_COL_TOKEN_BLIND, MONEY_TOKENS_COL_TOKEN_ID, MONEY_TOKENS_TABLE,
     },
     },
     Drk,
     Drk,
 };
 };
 
 
 impl Drk {
 impl Drk {
-    /// Import a token mint authority into the wallet
-    pub async fn import_mint_authority(&self, mint_authority: SecretKey) -> WalletDbResult<()> {
-        let token_id = TokenId::derive(mint_authority);
+    /// Auxiliary function to derive `TokenAttributes` for provided secret key and token blind.
+    fn derive_token_attributes(
+        &self,
+        mint_authority: SecretKey,
+        token_blind: BaseBlind,
+    ) -> TokenAttributes {
+        // Create the Auth FuncID
+        let auth_func_id = FuncRef {
+            contract_id: *MONEY_CONTRACT_ID,
+            func_code: MoneyFunction::AuthTokenMintV1 as u8,
+        }
+        .to_func_id();
+
+        // Grab the mint autority key public coordinates
+        let (mint_auth_x, mint_auth_y) = PublicKey::from_secret(mint_authority).xy();
+
+        // Generate the token attributes
+        TokenAttributes {
+            auth_parent: auth_func_id,
+            user_data: poseidon_hash([mint_auth_x, mint_auth_y]),
+            blind: token_blind,
+        }
+    }
+
+    /// Import a token mint authority into the wallet.
+    pub async fn import_mint_authority(
+        &self,
+        mint_authority: SecretKey,
+        token_blind: BaseBlind,
+    ) -> Result<TokenId> {
+        let token_id = self.derive_token_attributes(mint_authority, token_blind).to_token_id();
         let is_frozen = 0;
         let is_frozen = 0;
 
 
         let query = format!(
         let query = format!(
-            "INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
+            "INSERT INTO {} ({}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4);",
             *MONEY_TOKENS_TABLE,
             *MONEY_TOKENS_TABLE,
-            MONEY_TOKENS_COL_MINT_AUTHORITY,
             MONEY_TOKENS_COL_TOKEN_ID,
             MONEY_TOKENS_COL_TOKEN_ID,
+            MONEY_TOKENS_COL_MINT_AUTHORITY,
+            MONEY_TOKENS_COL_TOKEN_BLIND,
             MONEY_TOKENS_COL_IS_FROZEN,
             MONEY_TOKENS_COL_IS_FROZEN,
         );
         );
 
 
-        self.wallet
+        if let Err(e) = self
+            .wallet
             .exec_sql(
             .exec_sql(
                 &query,
                 &query,
                 rusqlite::params![
                 rusqlite::params![
-                    serialize_async(&mint_authority).await,
                     serialize_async(&token_id).await,
                     serialize_async(&token_id).await,
+                    serialize_async(&mint_authority).await,
+                    serialize_async(&token_blind).await,
                     is_frozen,
                     is_frozen,
                 ],
                 ],
             )
             )
             .await
             .await
+        {
+            return Err(Error::RusqliteError(format!(
+                "[import_mint_authority] Inserting mint authority failed: {e:?}"
+            )))
+        };
+
+        Ok(token_id)
     }
     }
 
 
-    pub async fn list_tokens(&self) -> Result<Vec<(TokenId, SecretKey, bool)>> {
+    pub async fn get_mint_authorities(&self) -> Result<Vec<(TokenId, SecretKey, BaseBlind, bool)>> {
         let rows = match self.wallet.query_multiple(&MONEY_TOKENS_TABLE, &[], &[]).await {
         let rows = match self.wallet.query_multiple(&MONEY_TOKENS_TABLE, &[], &[]).await {
             Ok(r) => r,
             Ok(r) => r,
             Err(e) => {
             Err(e) => {
                 return Err(Error::RusqliteError(format!(
                 return Err(Error::RusqliteError(format!(
-                    "[list_tokens] Tokens retrieval failed: {e:?}"
+                    "[get_mint_authorities] Tokens retrieval failed: {e:?}"
                 )))
                 )))
             }
             }
         };
         };
 
 
         let mut ret = Vec::with_capacity(rows.len());
         let mut ret = Vec::with_capacity(rows.len());
         for row in rows {
         for row in rows {
-            let Value::Blob(ref auth_bytes) = row[0] else {
-                return Err(Error::ParseFailed("[list_tokens] Mint authority bytes parsing failed"))
+            let Value::Blob(ref token_bytes) = row[0] else {
+                return Err(Error::ParseFailed(
+                    "[get_mint_authorities] Token ID bytes parsing failed",
+                ))
+            };
+            let token_id = deserialize_async(token_bytes).await?;
+
+            let Value::Blob(ref auth_bytes) = row[1] else {
+                return Err(Error::ParseFailed(
+                    "[get_mint_authorities] Mint authority bytes parsing failed",
+                ))
             };
             };
             let mint_authority = deserialize_async(auth_bytes).await?;
             let mint_authority = deserialize_async(auth_bytes).await?;
 
 
-            let Value::Blob(ref token_bytes) = row[1] else {
-                return Err(Error::ParseFailed("[list_tokens] Token ID bytes parsing failed"))
+            let Value::Blob(ref token_blind_bytes) = row[2] else {
+                return Err(Error::ParseFailed(
+                    "[get_mint_authorities] Token blind bytes parsing failed",
+                ))
             };
             };
-            let token_id = deserialize_async(token_bytes).await?;
+            let token_blind: BaseBlind = deserialize_async(token_blind_bytes).await?;
 
 
-            let Value::Integer(frozen) = row[2] else {
-                return Err(Error::ParseFailed("[list_tokens] Is frozen parsing failed"))
+            let Value::Integer(frozen) = row[3] else {
+                return Err(Error::ParseFailed("[get_mint_authorities] Is frozen parsing failed"))
             };
             };
             let Ok(frozen) = i32::try_from(frozen) else {
             let Ok(frozen) = i32::try_from(frozen) else {
-                return Err(Error::ParseFailed("[list_tokens] Is frozen parsing failed"))
+                return Err(Error::ParseFailed("[get_mint_authorities] Is frozen parsing failed"))
             };
             };
 
 
-            ret.push((token_id, mint_authority, frozen != 0));
+            ret.push((token_id, mint_authority, token_blind, frozen != 0));
         }
         }
 
 
         Ok(ret)
         Ok(ret)
@@ -129,7 +178,7 @@ impl Drk {
         let amount = decode_base10(amount, BALANCE_BASE10_DECIMALS, false)?;
         let amount = decode_base10(amount, BALANCE_BASE10_DECIMALS, false)?;
         let token_id = token_attrs.to_token_id();
         let token_id = token_attrs.to_token_id();
 
 
-        let mut tokens = self.list_tokens().await?;
+        let mut tokens = self.get_mint_authorities().await?;
         tokens.retain(|x| x.0 == token_id);
         tokens.retain(|x| x.0 == token_id);
         if tokens.is_empty() {
         if tokens.is_empty() {
             return Err(Error::Custom(format!(
             return Err(Error::Custom(format!(
@@ -140,7 +189,7 @@ impl Drk {
 
 
         let mint_authority = Keypair::new(tokens[0].1);
         let mint_authority = Keypair::new(tokens[0].1);
 
 
-        if tokens[0].2 {
+        if tokens[0].3 {
             return Err(Error::Custom(
             return Err(Error::Custom(
                 "This token mint is marked as frozen in the wallet".to_string(),
                 "This token mint is marked as frozen in the wallet".to_string(),
             ))
             ))
@@ -272,7 +321,7 @@ impl Drk {
     /// Create a token freeze transaction. Returns the transaction object on success.
     /// Create a token freeze transaction. Returns the transaction object on success.
     pub async fn freeze_token(&self, token_attrs: TokenAttributes) -> Result<Transaction> {
     pub async fn freeze_token(&self, token_attrs: TokenAttributes) -> Result<Transaction> {
         let token_id = token_attrs.to_token_id();
         let token_id = token_attrs.to_token_id();
-        let mut tokens = self.list_tokens().await?;
+        let mut tokens = self.get_mint_authorities().await?;
         tokens.retain(|x| x.0 == token_id);
         tokens.retain(|x| x.0 == token_id);
         if tokens.is_empty() {
         if tokens.is_empty() {
             return Err(Error::Custom(format!(
             return Err(Error::Custom(format!(
@@ -283,7 +332,7 @@ impl Drk {
 
 
         let mint_authority = Keypair::new(tokens[0].1);
         let mint_authority = Keypair::new(tokens[0].1);
 
 
-        if tokens[0].2 {
+        if tokens[0].3 {
             return Err(Error::Custom(
             return Err(Error::Custom(
                 "This token is already marked as frozen in the wallet".to_string(),
                 "This token is already marked as frozen in the wallet".to_string(),
             ))
             ))

+ 62 - 1
src/sdk/src/crypto/blind.rs

@@ -16,13 +16,20 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
  */
 
 
+use core::str::FromStr;
+
 #[cfg(feature = "async")]
 #[cfg(feature = "async")]
 use darkfi_serial::{async_trait, AsyncDecodable, AsyncEncodable};
 use darkfi_serial::{async_trait, AsyncDecodable, AsyncEncodable};
 use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable};
 use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable};
 
 
-use pasta_curves::{group::ff::Field, pallas};
+use pasta_curves::{
+    group::ff::{Field, PrimeField},
+    pallas,
+};
 use rand_core::{CryptoRng, RngCore};
 use rand_core::{CryptoRng, RngCore};
 
 
+use crate::error::ContractError;
+
 #[cfg(feature = "async")]
 #[cfg(feature = "async")]
 pub trait EncDecode: Encodable + Decodable + AsyncEncodable + AsyncDecodable {}
 pub trait EncDecode: Encodable + Decodable + AsyncEncodable + AsyncDecodable {}
 #[cfg(not(feature = "async"))]
 #[cfg(not(feature = "async"))]
@@ -72,8 +79,62 @@ impl From<u64> for BaseBlind {
     }
     }
 }
 }
 
 
+impl FromStr for BaseBlind {
+    type Err = ContractError;
+
+    /// Tries to create a `BaseBlind` object from a base58 encoded string.
+    fn from_str(enc: &str) -> Result<Self, Self::Err> {
+        let decoded = bs58::decode(enc).into_vec()?;
+        if decoded.len() != 32 {
+            return Err(Self::Err::IoError(
+                "Failed decoding BaseBlind from bytes, len is not 32".to_string(),
+            ))
+        }
+
+        match pallas::Base::from_repr(decoded.try_into().unwrap()).into() {
+            Some(k) => Ok(Self(k)),
+            None => Err(ContractError::IoError("Could not convert bytes to BaseBlind".to_string())),
+        }
+    }
+}
+
+impl core::fmt::Display for BaseBlind {
+    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+        let disp: String = bs58::encode(self.0.to_repr()).into_string();
+        write!(f, "{}", disp)
+    }
+}
+
 impl From<u64> for ScalarBlind {
 impl From<u64> for ScalarBlind {
     fn from(x: u64) -> Self {
     fn from(x: u64) -> Self {
         Self(pallas::Scalar::from(x))
         Self(pallas::Scalar::from(x))
     }
     }
 }
 }
+
+impl FromStr for ScalarBlind {
+    type Err = ContractError;
+
+    /// Tries to create a `ScalarBlind` object from a base58 encoded string.
+    fn from_str(enc: &str) -> Result<Self, Self::Err> {
+        let decoded = bs58::decode(enc).into_vec()?;
+        if decoded.len() != 32 {
+            return Err(Self::Err::IoError(
+                "Failed decoding ScalarBlind from bytes, len is not 32".to_string(),
+            ))
+        }
+
+        match pallas::Scalar::from_repr(decoded.try_into().unwrap()).into() {
+            Some(k) => Ok(Self(k)),
+            None => {
+                Err(ContractError::IoError("Could not convert bytes to ScalarBlind".to_string()))
+            }
+        }
+    }
+}
+
+impl core::fmt::Display for ScalarBlind {
+    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+        let disp: String = bs58::encode(self.0.to_repr()).into_string();
+        write!(f, "{}", disp)
+    }
+}