Jelajahi Sumber

sdk: Implement a macro for bs58 => pallas::Base and use it where applicable.

parazyd 3 tahun lalu
induk
melakukan
09eb0e1147

+ 3 - 1
bin/darkfid/src/rpc_blockchain.rs

@@ -16,6 +16,8 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+use std::str::FromStr;
+
 use darkfi_sdk::crypto::ContractId;
 use darkfi_serial::{deserialize, serialize};
 use log::{debug, error};
@@ -201,7 +203,7 @@ impl Darkfid {
             return JsonError::new(InvalidParams, None, id).into()
         }
 
-        let contract_id = match ContractId::try_from(params[0].as_str().unwrap()) {
+        let contract_id = match ContractId::from_str(params[0].as_str().unwrap()) {
             Ok(v) => v,
             Err(e) => {
                 error!("[RPC] blockchain.lookup_zkas: Error decoding string to ContractId: {}", e);

+ 4 - 3
bin/drk/src/main.rs

@@ -27,8 +27,9 @@ use anyhow::{anyhow, Context, Result};
 use clap::{CommandFactory, Parser, Subcommand};
 use clap_complete::{generate, Shell};
 use darkfi::{tx::Transaction, util::parse::decode_base10, zk::halo2::Field};
+use darkfi_money_contract::model::Coin;
 use darkfi_sdk::{
-    crypto::{Coin, PublicKey, SecretKey, TokenId},
+    crypto::{PublicKey, SecretKey, TokenId},
     pasta::{group::ff::PrimeField, pallas},
 };
 use darkfi_serial::{deserialize, serialize};
@@ -1197,7 +1198,7 @@ async fn main() -> Result<()> {
                 }
 
                 let token_id =
-                    TokenId::try_from(token.as_str()).with_context(|| "Invalid Token ID")?;
+                    TokenId::from_str(token.as_str()).with_context(|| "Invalid Token ID")?;
                 let drk = Drk::new(args.endpoint).await?;
                 drk.add_alias(alias, token_id).await?;
 
@@ -1207,7 +1208,7 @@ async fn main() -> Result<()> {
             AliasSubcmd::Show { alias, token } => {
                 let token_id = match token {
                     Some(t) => {
-                        Some(TokenId::try_from(t.as_str()).with_context(|| "Invalid Token ID")?)
+                        Some(TokenId::from_str(t.as_str()).with_context(|| "Invalid Token ID")?)
                     }
                     None => None,
                 };

+ 2 - 2
bin/drk/src/rpc_swap.rs

@@ -26,14 +26,14 @@ use darkfi::{
 };
 use darkfi_money_contract::{
     client::{swap_v1::SwapCallBuilder, MoneyNote},
-    model::MoneyTransferParamsV1,
+    model::{Coin, MoneyTransferParamsV1},
     MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };
 use darkfi_sdk::{
     crypto::{
         contract_id::MONEY_CONTRACT_ID,
         pedersen::{pedersen_commitment_base, pedersen_commitment_u64},
-        poseidon_hash, Coin, PublicKey, SecretKey, TokenId,
+        poseidon_hash, PublicKey, SecretKey, TokenId,
     },
     pasta::pallas,
     tx::ContractCall,

+ 8 - 5
bin/drk/src/wallet_money.rs

@@ -15,7 +15,8 @@
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
-use std::collections::HashMap;
+
+use std::{collections::HashMap, str::FromStr};
 
 use anyhow::{anyhow, Result};
 use darkfi::{rpc::jsonrpc::JsonRequest, tx::Transaction, wallet::walletdb::QueryType};
@@ -32,13 +33,15 @@ use darkfi_money_contract::{
         MONEY_KEYS_COL_SECRET, MONEY_KEYS_TABLE, MONEY_TOKENS_COL_IS_FROZEN,
         MONEY_TOKENS_COL_TOKEN_ID, MONEY_TOKENS_TABLE, MONEY_TREE_COL_TREE, MONEY_TREE_TABLE,
     },
-    model::{MoneyTokenFreezeParamsV1, MoneyTokenMintParamsV1, MoneyTransferParamsV1, Output},
+    model::{
+        Coin, MoneyTokenFreezeParamsV1, MoneyTokenMintParamsV1, MoneyTransferParamsV1, Output,
+    },
     MoneyFunction,
 };
 use darkfi_sdk::{
     crypto::{
-        poseidon_hash, Coin, Keypair, MerkleNode, MerkleTree, Nullifier, PublicKey, SecretKey,
-        TokenId, MONEY_CONTRACT_ID,
+        poseidon_hash, Keypair, MerkleNode, MerkleTree, Nullifier, PublicKey, SecretKey, TokenId,
+        MONEY_CONTRACT_ID,
     },
     incrementalmerkletree,
     incrementalmerkletree::Tree,
@@ -766,7 +769,7 @@ impl Drk {
             }
         }
         // Else parse input
-        Ok(TokenId::try_from(input.as_str())?)
+        Ok(TokenId::from_str(input.as_str())?)
     }
 
     /// Create an alias record for provided Token ID

+ 2 - 2
src/contract/consensus/src/client/common.rs

@@ -23,10 +23,10 @@ use darkfi::{
     zkas::ZkBinary,
     Result,
 };
-use darkfi_money_contract::client::ConsensusNote;
+use darkfi_money_contract::{client::ConsensusNote, model::Coin};
 use darkfi_sdk::{
     crypto::{
-        pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, Coin, MerkleNode, MerklePosition,
+        pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, MerkleNode, MerklePosition,
         Nullifier, PublicKey, SecretKey,
     },
     incrementalmerkletree::Hashable,

+ 2 - 2
src/contract/consensus/src/client/proposal_v1.rs

@@ -26,12 +26,12 @@ use darkfi::{
 };
 use darkfi_money_contract::{
     client::{ConsensusNote, ConsensusOwnCoin},
-    model::{ConsensusInput, ConsensusOutput, PALLAS_ZERO},
+    model::{Coin, ConsensusInput, ConsensusOutput, PALLAS_ZERO},
 };
 use darkfi_sdk::{
     crypto::{
         ecvrf::VrfProof, note::AeadEncryptedNote, pasta_prelude::*, pedersen_commitment_base,
-        pedersen_commitment_u64, poseidon_hash, Coin, MerkleNode, MerkleTree, Nullifier, PublicKey,
+        pedersen_commitment_u64, poseidon_hash, MerkleNode, MerkleTree, Nullifier, PublicKey,
         SecretKey,
     },
     incrementalmerkletree::{Hashable, Tree},

+ 4 - 2
src/contract/consensus/src/model.rs

@@ -16,9 +16,11 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use darkfi_money_contract::model::{ClearInput, ConsensusInput, ConsensusOutput, Input, Output};
+use darkfi_money_contract::model::{
+    ClearInput, Coin, ConsensusInput, ConsensusOutput, Input, Output,
+};
 use darkfi_sdk::{
-    crypto::{ecvrf::VrfProof, Coin, Nullifier, PublicKey},
+    crypto::{ecvrf::VrfProof, Nullifier, PublicKey},
     pasta::pallas,
 };
 use darkfi_serial::{SerialDecodable, SerialEncodable};

+ 3 - 3
src/contract/consensus/tests/harness.rs

@@ -34,8 +34,8 @@ use darkfi::{
 };
 use darkfi_sdk::{
     crypto::{
-        merkle_prelude::*, poseidon_hash, Coin, Keypair, MerkleNode, MerkleTree, Nullifier,
-        PublicKey, SecretKey, CONSENSUS_CONTRACT_ID, DARK_TOKEN_ID, MONEY_CONTRACT_ID,
+        merkle_prelude::*, poseidon_hash, Keypair, MerkleNode, MerkleTree, Nullifier, PublicKey,
+        SecretKey, CONSENSUS_CONTRACT_ID, DARK_TOKEN_ID, MONEY_CONTRACT_ID,
     },
     pasta::pallas,
     ContractCall,
@@ -59,7 +59,7 @@ use darkfi_money_contract::{
         unstake_v1::MoneyUnstakeCallBuilder, ConsensusNote, ConsensusOwnCoin, MoneyNote, OwnCoin,
     },
     model::{
-        ConsensusOutput, ConsensusStakeParamsV1, MoneyTransferParamsV1, MoneyUnstakeParamsV1,
+        Coin, ConsensusOutput, ConsensusStakeParamsV1, MoneyTransferParamsV1, MoneyUnstakeParamsV1,
         Output,
     },
     MoneyFunction, CONSENSUS_CONTRACT_ZKAS_BURN_NS_V1, CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1,

+ 2 - 3
src/contract/dao/src/wallet_cache.rs

@@ -17,11 +17,10 @@
  */
 
 use darkfi_sdk::crypto::{
-    merkle_prelude::*, note::AeadEncryptedNote, Coin, MerkleNode, MerklePosition, MerkleTree,
-    SecretKey,
+    merkle_prelude::*, note::AeadEncryptedNote, MerkleNode, MerklePosition, MerkleTree, SecretKey,
 };
 
-use darkfi_money_contract::client::MoneyNote;
+use darkfi_money_contract::{client::MoneyNote, model::Coin};
 
 pub struct OwnCoin {
     pub coin: Coin,

+ 4 - 4
src/contract/dao/tests/integration.rs

@@ -21,7 +21,7 @@ use std::time::{Duration, Instant};
 use darkfi::{tx::Transaction, Result};
 use darkfi_sdk::{
     crypto::{
-        merkle_prelude::*, pallas, pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, Coin,
+        merkle_prelude::*, pallas, pasta_prelude::*, pedersen_commitment_u64, poseidon_hash,
         Keypair, MerkleNode, MerkleTree, SecretKey, TokenId, DAO_CONTRACT_ID, DARK_TOKEN_ID,
         MONEY_CONTRACT_ID,
     },
@@ -37,7 +37,7 @@ use darkfi_dao_contract::{
 
 use darkfi_money_contract::{
     client::token_mint_v1::TokenMintCallBuilder,
-    model::{MoneyTokenMintParamsV1, MoneyTransferParamsV1},
+    model::{Coin, MoneyTokenMintParamsV1, MoneyTransferParamsV1},
     MoneyFunction,
 };
 
@@ -235,7 +235,7 @@ async fn integration_test() -> Result<()> {
         treasury_note.user_data,
         treasury_note.coin_blind,
     ]);
-    assert_eq!(coin, dao_recv_coin.coin.0);
+    assert_eq!(coin, dao_recv_coin.coin.inner());
 
     assert_eq!(treasury_note.spend_hook, spend_hook);
     assert_eq!(treasury_note.user_data, dao_bulla.inner());
@@ -356,7 +356,7 @@ async fn integration_test() -> Result<()> {
                 note.user_data,
                 note.coin_blind,
             ]);
-            assert_eq!(coin, recv_coin.coin.0);
+            assert_eq!(coin, recv_coin.coin.inner());
 
             debug!(target: "dao", "Holder{} received a coin worth {} gDRK", i, note.value);
 

+ 1 - 1
src/contract/money/Cargo.toml

@@ -9,6 +9,7 @@ edition = "2021"
 crate-type = ["cdylib", "rlib"]
 
 [dependencies]
+bs58 = "0.4.0"
 darkfi-sdk = { path = "../../sdk" }
 darkfi-serial = { path = "../../serial", features = ["derive", "crypto"] }
 thiserror = "1.0.40"
@@ -24,7 +25,6 @@ rand = { version = "0.8.5", optional = true }
 # These are used just for the integration tests
 [dev-dependencies]
 async-std = {version = "1.12.0", features = ["attributes"]}
-bs58 = "0.4.0"
 darkfi = {path = "../../../", features = ["tx", "blockchain"]}
 simplelog = "0.12.1"
 sled = "0.34.7"

+ 2 - 2
src/contract/money/src/client/genesis_mint_v1.rs

@@ -22,7 +22,7 @@ use darkfi::{
     Result,
 };
 use darkfi_sdk::{
-    crypto::{note::AeadEncryptedNote, pasta_prelude::*, Coin, Keypair, PublicKey, DARK_TOKEN_ID},
+    crypto::{note::AeadEncryptedNote, pasta_prelude::*, Keypair, PublicKey, DARK_TOKEN_ID},
     pasta::pallas,
 };
 use log::{debug, info};
@@ -36,7 +36,7 @@ use crate::{
         },
         MoneyNote,
     },
-    model::{ClearInput, MoneyTokenMintParamsV1, Output},
+    model::{ClearInput, Coin, MoneyTokenMintParamsV1, Output},
 };
 
 pub struct GenesisMintCallDebris {

+ 2 - 2
src/contract/money/src/client/mod.rs

@@ -27,12 +27,12 @@
 //! are able to abstract away any wallet interfaces to client implementations.
 
 use darkfi_sdk::{
-    crypto::{Coin, MerklePosition, Nullifier, SecretKey, TokenId, DARK_TOKEN_ID},
+    crypto::{MerklePosition, Nullifier, SecretKey, TokenId, DARK_TOKEN_ID},
     pasta::pallas,
 };
 use darkfi_serial::{SerialDecodable, SerialEncodable};
 
-use crate::model::{PALLAS_ZERO, SCALAR_ZERO};
+use crate::model::{Coin, PALLAS_ZERO, SCALAR_ZERO};
 
 /// `Money::TransferV1` API
 pub mod transfer_v1;

+ 2 - 2
src/contract/money/src/client/token_mint_v1.rs

@@ -24,7 +24,7 @@ use darkfi::{
 use darkfi_sdk::{
     crypto::{
         note::AeadEncryptedNote, pasta_prelude::*, pedersen_commitment_base,
-        pedersen_commitment_u64, poseidon_hash, Coin, Keypair, PublicKey, TokenId,
+        pedersen_commitment_u64, poseidon_hash, Keypair, PublicKey, TokenId,
     },
     pasta::pallas,
 };
@@ -36,7 +36,7 @@ use crate::{
         transfer_v1::{TransactionBuilderClearInputInfo, TransactionBuilderOutputInfo},
         MoneyNote,
     },
-    model::{ClearInput, MoneyTokenMintParamsV1, Output},
+    model::{ClearInput, Coin, MoneyTokenMintParamsV1, Output},
 };
 
 pub struct TokenMintCallDebris {

+ 3 - 3
src/contract/money/src/client/transfer_v1.rs

@@ -26,8 +26,8 @@ use darkfi::{
 use darkfi_sdk::{
     crypto::{
         note::AeadEncryptedNote, pasta_prelude::*, pedersen_commitment_base,
-        pedersen_commitment_u64, poseidon_hash, Coin, Keypair, MerkleNode, MerklePosition,
-        MerkleTree, Nullifier, PublicKey, SecretKey, TokenId,
+        pedersen_commitment_u64, poseidon_hash, Keypair, MerkleNode, MerklePosition, MerkleTree,
+        Nullifier, PublicKey, SecretKey, TokenId,
     },
     incrementalmerkletree::{Hashable, Tree},
     pasta::pallas,
@@ -37,7 +37,7 @@ use rand::rngs::OsRng;
 
 use crate::{
     client::{MoneyNote, OwnCoin},
-    model::{ClearInput, Input, MoneyTransferParamsV1, Output},
+    model::{ClearInput, Coin, Input, MoneyTransferParamsV1, Output},
 };
 
 pub struct TransferCallDebris {

+ 2 - 2
src/contract/money/src/client/unstake_v1.rs

@@ -26,7 +26,7 @@ use darkfi::{
 use darkfi_sdk::{
     crypto::{
         note::AeadEncryptedNote, pasta_prelude::*, pedersen_commitment_base,
-        pedersen_commitment_u64, poseidon_hash, Coin, MerkleNode, Nullifier, PublicKey, TokenId,
+        pedersen_commitment_u64, poseidon_hash, MerkleNode, Nullifier, PublicKey, TokenId,
         DARK_TOKEN_ID,
     },
     pasta::pallas,
@@ -36,7 +36,7 @@ use rand::rngs::OsRng;
 
 use crate::{
     client::{ConsensusOwnCoin, MoneyNote},
-    model::{ConsensusInput, MoneyUnstakeParamsV1, Output, PALLAS_ZERO, SCALAR_ZERO},
+    model::{Coin, ConsensusInput, MoneyUnstakeParamsV1, Output, PALLAS_ZERO, SCALAR_ZERO},
 };
 
 pub struct MoneyUnstakeCallDebris {

+ 37 - 1
src/contract/money/src/model.rs

@@ -17,11 +17,47 @@
  */
 
 use darkfi_sdk::{
-    crypto::{note::AeadEncryptedNote, Coin, MerkleNode, Nullifier, PublicKey, TokenId},
+    crypto::{
+        note::AeadEncryptedNote, pasta_prelude::PrimeField, MerkleNode, Nullifier, PublicKey,
+        TokenId,
+    },
+    error::ContractError,
     pasta::pallas,
 };
 use darkfi_serial::{SerialDecodable, SerialEncodable};
 
+/// A `Coin` represented in the Money state
+#[derive(Debug, Clone, Copy, Eq, PartialEq, SerialEncodable, SerialDecodable)]
+pub struct Coin(pallas::Base);
+
+impl Coin {
+    /// Reference the raw inner base field element
+    pub fn inner(&self) -> pallas::Base {
+        self.0
+    }
+
+    /// Create a `Coin` object from given bytes, erroring if the input
+    /// bytes are noncanonical.
+    pub fn from_bytes(x: [u8; 32]) -> Result<Self, ContractError> {
+        match pallas::Base::from_repr(x).into() {
+            Some(v) => Ok(Self(v)),
+            None => {
+                Err(ContractError::IoError("Failed to instantiate Coin from bytes".to_string()))
+            }
+        }
+    }
+
+    /// Convert the `Coin` type into 32 raw bytes
+    pub fn to_bytes(&self) -> [u8; 32] {
+        self.0.to_repr()
+    }
+}
+
+use core::str::FromStr;
+darkfi_sdk::fp_from_bs58!(Coin);
+darkfi_sdk::fp_to_bs58!(Coin);
+darkfi_sdk::ty_from_fp!(Coin);
+
 /// A contract call's clear input
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
 pub struct ClearInput {

+ 2 - 1
src/contract/money/tests/genesis_mint.rs

@@ -30,7 +30,7 @@ use std::time::{Duration, Instant};
 use darkfi::{tx::Transaction, Result};
 use darkfi_sdk::{
     crypto::{
-        merkle_prelude::*, pallas, pasta_prelude::*, poseidon_hash, Coin, MerkleNode, Nullifier,
+        merkle_prelude::*, pallas, pasta_prelude::*, poseidon_hash, MerkleNode, Nullifier,
         MONEY_CONTRACT_ID,
     },
     ContractCall,
@@ -44,6 +44,7 @@ use darkfi_money_contract::{
         genesis_mint_v1::GenesisMintCallBuilder, transfer_v1::TransferCallBuilder, MoneyNote,
         OwnCoin,
     },
+    model::Coin,
     MoneyFunction::{GenesisMintV1 as GenesisMint, TransferV1 as MoneyTransfer},
     MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };

+ 2 - 2
src/contract/money/tests/mint_pay_swap.rs

@@ -32,7 +32,7 @@ use std::time::{Duration, Instant};
 use darkfi::{tx::Transaction, Result};
 use darkfi_sdk::{
     crypto::{
-        merkle_prelude::*, pallas, pasta_prelude::*, poseidon_hash, Coin, MerkleNode, Nullifier,
+        merkle_prelude::*, pallas, pasta_prelude::*, poseidon_hash, MerkleNode, Nullifier,
         ValueBlind, MONEY_CONTRACT_ID,
     },
     ContractCall,
@@ -43,7 +43,7 @@ use rand::rngs::OsRng;
 
 use darkfi_money_contract::{
     client::{swap_v1::SwapCallBuilder, transfer_v1::TransferCallBuilder, MoneyNote, OwnCoin},
-    model::MoneyTransferParamsV1 as MoneyTransferParams,
+    model::{Coin, MoneyTransferParamsV1 as MoneyTransferParams},
     MoneyFunction::{OtcSwapV1 as MoneyOtcSwap, TransferV1 as MoneyTransfer},
     MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };

+ 2 - 1
src/contract/money/tests/txs_verification.rs

@@ -27,7 +27,7 @@
 use darkfi::{tx::Transaction, Result};
 use darkfi_sdk::{
     crypto::{
-        merkle_prelude::*, pallas, pasta_prelude::*, poseidon_hash, Coin, MerkleNode, Nullifier,
+        merkle_prelude::*, pallas, pasta_prelude::*, poseidon_hash, MerkleNode, Nullifier,
         MONEY_CONTRACT_ID,
     },
     ContractCall,
@@ -38,6 +38,7 @@ use rand::rngs::OsRng;
 
 use darkfi_money_contract::{
     client::{transfer_v1::TransferCallBuilder, MoneyNote, OwnCoin},
+    model::Coin,
     MoneyFunction::TransferV1 as MoneyTransfer,
     MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };

+ 2 - 1
src/contract/money/tests/verification_bench.rs

@@ -21,7 +21,7 @@ use std::{env, str::FromStr};
 use darkfi::{tx::Transaction, Result};
 use darkfi_sdk::{
     crypto::{
-        merkle_prelude::*, pallas, pasta_prelude::*, poseidon_hash, Coin, MerkleNode, Nullifier,
+        merkle_prelude::*, pallas, pasta_prelude::*, poseidon_hash, MerkleNode, Nullifier,
         MONEY_CONTRACT_ID,
     },
     ContractCall,
@@ -32,6 +32,7 @@ use rand::{prelude::IteratorRandom, rngs::OsRng, Rng};
 
 use darkfi_money_contract::{
     client::{transfer_v1::TransferCallBuilder, MoneyNote, OwnCoin},
+    model::Coin,
     MoneyFunction::TransferV1 as MoneyTransfer,
     MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };

+ 0 - 85
src/sdk/src/crypto/coin.rs

@@ -1,85 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use core::{fmt, str::FromStr};
-use std::io;
-
-use darkfi_serial::{SerialDecodable, SerialEncodable};
-use pasta_curves::{group::ff::PrimeField, pallas};
-
-/// `Coin` is represented as a base field element.
-#[repr(C)]
-#[derive(Debug, Clone, Copy, Eq, PartialEq, SerialEncodable, SerialDecodable)]
-pub struct Coin(pub pallas::Base);
-
-impl Coin {
-    /// Reference the raw inner base field element
-    pub fn inner(&self) -> pallas::Base {
-        self.0
-    }
-
-    /// Try to create a `Coin` type from the given 32 bytes.
-    /// Returns `Some` if the bytes fit in the base field, and `None` if not.
-    pub fn from_bytes(bytes: [u8; 32]) -> Option<Self> {
-        let n = pallas::Base::from_repr(bytes);
-        match bool::from(n.is_some()) {
-            true => Some(Self(n.unwrap())),
-            false => None,
-        }
-    }
-
-    /// Convert the `Coin` type into 32 raw bytes
-    pub fn to_bytes(&self) -> [u8; 32] {
-        self.0.to_repr()
-    }
-}
-
-impl From<pallas::Base> for Coin {
-    fn from(x: pallas::Base) -> Self {
-        Self(x)
-    }
-}
-
-impl fmt::Display for Coin {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", bs58::encode(self.to_bytes()).into_string())
-    }
-}
-
-impl FromStr for Coin {
-    type Err = io::Error;
-
-    /// Tries to decode a base58 string into a `Coin` type.
-    /// This string is the same string received by calling `Coin::to_string()`.
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let bytes = match bs58::decode(s).into_vec() {
-            Ok(v) => v,
-            Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
-        };
-
-        if bytes.len() != 32 {
-            return Err(io::Error::new(io::ErrorKind::Other, "Length of decoded bytes is not 32"))
-        }
-
-        if let Some(coin) = Self::from_bytes(bytes.try_into().unwrap()) {
-            return Ok(coin)
-        }
-
-        Err(io::Error::new(io::ErrorKind::Other, "Invalid bytes for Coin"))
-    }
-}

+ 4 - 44
src/sdk/src/crypto/contract_id.rs

@@ -100,47 +100,7 @@ impl ContractId {
     }
 }
 
-impl From<pallas::Base> for ContractId {
-    fn from(x: pallas::Base) -> Self {
-        Self(x)
-    }
-}
-
-impl core::fmt::Display for ContractId {
-    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
-        // Base58 encoding
-        let contractid: String = bs58::encode(self.0.to_repr()).into_string();
-        write!(f, "{}", contractid)
-    }
-}
-
-impl TryFrom<&str> for ContractId {
-    type Error = ContractError;
-
-    fn try_from(s: &str) -> Result<Self, Self::Error> {
-        let bytes: [u8; 32] = match bs58::decode(s).into_vec() {
-            Ok(v) => {
-                if v.len() != 32 {
-                    return Err(ContractError::IoError(
-                        "Decoded bs58 string for ContractId is not 32 bytes long".to_string(),
-                    ))
-                }
-
-                v.try_into().unwrap()
-            }
-            Err(e) => {
-                return Err(ContractError::IoError(format!(
-                    "Failed to decode bs58 for ContractId: {}",
-                    e
-                )))
-            }
-        };
-
-        match pallas::Base::from_repr(bytes).into() {
-            Some(v) => Ok(Self(v)),
-            None => {
-                Err(ContractError::IoError("Bytes for ContractId are noncanonical".to_string()))
-            }
-        }
-    }
-}
+use core::str::FromStr;
+crate::fp_from_bs58!(ContractId);
+crate::fp_to_bs58!(ContractId);
+crate::ty_from_fp!(ContractId);

+ 46 - 4
src/sdk/src/crypto/mod.rs

@@ -30,10 +30,6 @@ pub use util::poseidon_hash;
 pub mod keypair;
 pub use keypair::{Keypair, PublicKey, SecretKey};
 
-/// Coin definitions and methods
-pub mod coin;
-pub use coin::Coin;
-
 /// Contract ID definitions and methods
 pub mod contract_id;
 pub use contract_id::{ContractId, CONSENSUS_CONTRACT_ID, DAO_CONTRACT_ID, MONEY_CONTRACT_ID};
@@ -88,3 +84,49 @@ pub mod pasta_prelude {
         },
     };
 }
+
+#[macro_export]
+macro_rules! fp_from_bs58 {
+    ($ty:ident) => {
+        impl FromStr for $ty {
+            type Err = ContractError;
+
+            fn from_str(s: &str) -> Result<Self, Self::Err> {
+                let bytes = match bs58::decode(s).into_vec() {
+                    Ok(v) => v,
+                    Err(e) => return Err(ContractError::IoError(e.to_string())),
+                };
+
+                if bytes.len() != 32 {
+                    return Err(ContractError::IoError(
+                        "Length of decoded bytes is not 32".to_string(),
+                    ))
+                }
+
+                Self::from_bytes(bytes.try_into().unwrap())
+            }
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! fp_to_bs58 {
+    ($ty:ident) => {
+        impl std::fmt::Display for $ty {
+            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+                write!(f, "{}", bs58::encode(self.to_bytes()).into_string())
+            }
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! ty_from_fp {
+    ($ty:ident) => {
+        impl From<pallas::Base> for $ty {
+            fn from(x: pallas::Base) -> Self {
+                Self(x)
+            }
+        }
+    };
+}

+ 11 - 44
src/sdk/src/crypto/nullifier.rs

@@ -16,12 +16,11 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use core::{fmt, str::FromStr};
-use std::io;
-
 use darkfi_serial::{SerialDecodable, SerialEncodable};
 use pasta_curves::{group::ff::PrimeField, pallas};
 
+use crate::error::ContractError;
+
 /// The `Nullifier` is represented as a base field element.
 #[repr(C)]
 #[derive(Debug, Clone, Copy, Eq, PartialEq, SerialEncodable, SerialDecodable)]
@@ -33,13 +32,11 @@ impl Nullifier {
         self.0
     }
 
-    /// Try to create a `Nullifier` type from the given 32 bytes.
-    /// Returns `Some` if the bytes fit in the base field, and `None` if not.
-    pub fn from_bytes(bytes: [u8; 32]) -> Option<Self> {
-        let n = pallas::Base::from_repr(bytes);
-        match bool::from(n.is_some()) {
-            true => Some(Self(n.unwrap())),
-            false => None,
+    /// Create a `Nullifier` object from given bytes
+    pub fn from_bytes(x: [u8; 32]) -> Result<Self, ContractError> {
+        match pallas::Base::from_repr(x).into() {
+            Some(v) => Ok(Self(v)),
+            None => Err(ContractError::IoError("Noncanonical bytes for Nullifier".to_string())),
         }
     }
 
@@ -49,37 +46,7 @@ impl Nullifier {
     }
 }
 
-impl From<pallas::Base> for Nullifier {
-    fn from(x: pallas::Base) -> Self {
-        Self(x)
-    }
-}
-
-impl fmt::Display for Nullifier {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", bs58::encode(self.to_bytes()).into_string())
-    }
-}
-
-impl FromStr for Nullifier {
-    type Err = io::Error;
-
-    /// Tries to decode a base58 string into a `Nullifier` type.
-    /// This string is the same string received by calling `Nullifier::to_string()`.
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let bytes = match bs58::decode(s).into_vec() {
-            Ok(v) => v,
-            Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
-        };
-
-        if bytes.len() != 32 {
-            return Err(io::Error::new(io::ErrorKind::Other, "Length of decoded bytes is not 32"))
-        }
-
-        if let Some(nullifier) = Self::from_bytes(bytes.try_into().unwrap()) {
-            return Ok(nullifier)
-        }
-
-        Err(io::Error::new(io::ErrorKind::Other, "Invalid bytes for Nullifier"))
-    }
-}
+use core::str::FromStr;
+crate::fp_from_bs58!(Nullifier);
+crate::fp_to_bs58!(Nullifier);
+crate::ty_from_fp!(Nullifier);

+ 7 - 40
src/sdk/src/crypto/token_id.rs

@@ -72,47 +72,14 @@ impl TokenId {
             }
         }
     }
-}
-
-impl From<pallas::Base> for TokenId {
-    fn from(x: pallas::Base) -> Self {
-        Self(x)
-    }
-}
 
-impl core::fmt::Display for TokenId {
-    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
-        // Base58 encoding
-        let tokenid: String = bs58::encode(self.0.to_repr()).into_string();
-        write!(f, "{}", tokenid)
+    /// Convert the `TokenId` type into 32 raw bytes
+    pub fn to_bytes(&self) -> [u8; 32] {
+        self.0.to_repr()
     }
 }
 
-impl TryFrom<&str> for TokenId {
-    type Error = ContractError;
-
-    fn try_from(s: &str) -> Result<Self, Self::Error> {
-        let bytes: [u8; 32] = match bs58::decode(s).into_vec() {
-            Ok(v) => {
-                if v.len() != 32 {
-                    return Err(ContractError::IoError(
-                        "Decoded bs58 string for TokenId is not 32 bytes long".to_string(),
-                    ))
-                }
-
-                v.try_into().unwrap()
-            }
-            Err(e) => {
-                return Err(ContractError::IoError(format!(
-                    "Failed to decode bs58 for TokenId: {}",
-                    e
-                )))
-            }
-        };
-
-        match pallas::Base::from_repr(bytes).into() {
-            Some(v) => Ok(Self(v)),
-            None => Err(ContractError::IoError("Bytes for TokenId are noncanonical".to_string())),
-        }
-    }
-}
+use core::str::FromStr;
+crate::fp_from_bs58!(TokenId);
+crate::fp_to_bs58!(TokenId);
+crate::ty_from_fp!(TokenId);