diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f522d468e..4c74c5c57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,8 +35,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - run: rustup update nightly && rustup default nightly - - run: RUSTDOCFLAGS="-D warnings --cfg doc_cfg" cargo +nightly doc --workspace --all-features --no-deps --document-private-items + - run: rustup update stable && rustup default stable + - run: cargo +stable doc --workspace --all-features --no-deps --document-private-items lint: name: Lint (${{ matrix.os }} + ${{ matrix.channel }}) needs: [format, format-cargo-toml, docs] diff --git a/CHANGELOG.md b/CHANGELOG.md index 17bfa5430..70faa2e5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] ### Added +- [\#314](https://github.com/Manta-Network/manta-rs/pull/314) Prepares the signer export to wasm. - [\#289](https://github.com/Manta-Network/manta-rs/pull/289) AssetMetadata upgrade and NFT support. - [\#310](https://github.com/Manta-Network/manta-rs/pull/310) Add identity verification algorithm using ToPublic circuit diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index f61ad2645..1a797bb56 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -738,4 +738,43 @@ where { self.reconstruct_utxo(parameters, address).eq(utxos) } + + /// Returns the [`TransferShape`] of `self`. + #[inline] + pub fn shape(&self) -> TransferShape { + match self { + Self::ToPrivate(_, _) => TransferShape::ToPrivate, + Self::PrivateTransfer(_) => TransferShape::PrivateTransfer, + Self::ToPublic(_, _) => TransferShape::ToPublic, + } + } + + /// Verifies `self` against `transferpost`. + #[inline] + pub fn verify( + &self, + parameters: &Parameters, + address: &Address, + transferpost: TransferPost, + ) -> bool + where + Utxo: PartialEq, + { + if !TransferShape::from_post(&transferpost) + .map(|shape| shape.eq(&self.shape())) + .unwrap_or(false) + { + return false; + } + self.check_transaction_data( + parameters, + address, + &transferpost + .body + .receiver_posts + .into_iter() + .map(|receiver_post| receiver_post.utxo) + .collect(), + ) + } } diff --git a/manta-accounting/src/transfer/utxo/protocol.rs b/manta-accounting/src/transfer/utxo/protocol.rs index 8780353ba..8860e21a3 100644 --- a/manta-accounting/src/transfer/utxo/protocol.rs +++ b/manta-accounting/src/transfer/utxo/protocol.rs @@ -2397,9 +2397,11 @@ where Default(bound = "UtxoCommitmentRandomness: Default"), Eq(bound = "UtxoCommitmentRandomness: Eq"), Hash(bound = "UtxoCommitmentRandomness: Hash"), + Ord(bound = "UtxoCommitmentRandomness: Ord"), PartialEq( bound = "UtxoCommitmentRandomness: core::cmp::PartialEq>" - ) + ), + PartialOrd(bound = "UtxoCommitmentRandomness: PartialOrd") )] pub struct Identifier where diff --git a/manta-accounting/src/wallet/mod.rs b/manta-accounting/src/wallet/mod.rs index fe6c36eec..d12a29bf3 100644 --- a/manta-accounting/src/wallet/mod.rs +++ b/manta-accounting/src/wallet/mod.rs @@ -38,8 +38,8 @@ use crate::{ ledger::ReadResponse, signer::{ BalanceUpdate, IdentityRequest, IdentityResponse, SignError, SignRequest, SignResponse, - SyncData, SyncError, SyncRequest, SyncResponse, TransactionDataRequest, - TransactionDataResponse, + SignWithTransactionDataResponse, SyncData, SyncError, SyncRequest, SyncResponse, + TransactionDataRequest, TransactionDataResponse, }, }, }; @@ -386,7 +386,8 @@ where .map_err(Error::SignError) } - /// Attempts to process TransferPosts and returns the corresponding TransactionData. + /// Attempts to process [`TransferPost`]s and returns the corresponding + /// [`TransactionData`](crate::transfer::canonical::TransactionData). #[inline] pub async fn transaction_data( &mut self, @@ -456,6 +457,29 @@ where pub async fn address(&mut self) -> Result, S::Error> { self.signer.address().await } + + /// Signs `transaction` and returns the [`TransferPost`]s and the + /// associated [`TransactionData`](crate::transfer::canonical::TransactionData) if successful. + #[inline] + pub async fn sign_with_transaction_data( + &mut self, + transaction: Transaction, + metadata: Option, + ) -> Result, Error> + where + TransferPost: Clone, + { + self.check(&transaction) + .map_err(Error::InsufficientBalance)?; + self.signer + .sign_with_transaction_data(SignRequest { + transaction, + metadata, + }) + .await + .map_err(Error::SignerConnectionError)? + .map_err(Error::SignError) + } } /// Inconsistency Error diff --git a/manta-accounting/src/wallet/signer/functions.rs b/manta-accounting/src/wallet/signer/functions.rs new file mode 100644 index 000000000..bbdb8ca5c --- /dev/null +++ b/manta-accounting/src/wallet/signer/functions.rs @@ -0,0 +1,917 @@ +// Copyright 2019-2022 Manta Network. +// This file is part of manta-rs. +// +// manta-rs is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// manta-rs 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with manta-rs. If not, see . + +//! Signer Functions + +use crate::{ + asset::AssetMap, + key::{Account, DeriveAddress}, + transfer::{ + self, + batch::Join, + canonical::{ + MultiProvingContext, PrivateTransfer, PrivateTransferShape, Selection, ToPrivate, + ToPublic, Transaction, TransactionData, TransferShape, + }, + receiver::ReceiverPost, + requires_authorization, + utxo::{auth::DeriveContext, DeriveDecryptionKey, DeriveSpend, Spend, UtxoReconstruct}, + Address, Asset, AssociatedData, Authorization, AuthorizationContext, FullParametersRef, + IdentifiedAsset, Identifier, IdentityProof, Note, Nullifier, Parameters, PreSender, + ProvingContext, Receiver, Sender, Shape, SpendingKey, Transfer, TransferPost, Utxo, + UtxoAccumulatorItem, UtxoAccumulatorModel, + }, + wallet::signer::{ + AccountTable, BalanceUpdate, Checkpoint, Configuration, SignError, SignResponse, + SignWithTransactionDataResponse, SignWithTransactionDataResult, SignerParameters, SyncData, + SyncError, SyncRequest, SyncResponse, + }, +}; +use alloc::{vec, vec::Vec}; +use manta_crypto::{ + accumulator::{Accumulator, ItemHashFunction, OptimizedAccumulator}, + rand::Rand, +}; +use manta_util::{ + array_map, cmp::Independence, into_array_unchecked, iter::IteratorExt, persistence::Rollback, + vec::VecExt, +}; + +/// Returns the default account for `accounts`. +#[inline] +pub fn default_account(accounts: &AccountTable) -> Account +where + C: Configuration, +{ + accounts.get_default() +} + +/// Returns the default spending key for `accounts`. +#[inline] +fn default_spending_key(accounts: &AccountTable, parameters: &C::Parameters) -> SpendingKey +where + C: Configuration, +{ + let _ = parameters; + accounts.get_default().spending_key() +} + +/// Returns the default authorization context for `accounts`. +#[inline] +fn default_authorization_context( + accounts: &AccountTable, + parameters: &C::Parameters, +) -> AuthorizationContext +where + C: Configuration, +{ + parameters.derive_context(&default_spending_key::(accounts, parameters)) +} + +/// Returns the authorization for the default spending key of `accounts`. +#[inline] +fn authorization_for_default_spending_key( + accounts: &AccountTable, + parameters: &C::Parameters, + rng: &mut C::Rng, +) -> Authorization +where + C: Configuration, +{ + Authorization::::from_spending_key( + parameters, + &default_spending_key::(accounts, parameters), + rng, + ) +} + +/// Returns the address for the default account of `accounts`. +#[inline] +fn default_address(accounts: &AccountTable, parameters: &C::Parameters) -> Address +where + C: Configuration, +{ + accounts.get_default().address(parameters) +} + +/// Hashes `utxo` using the [`UtxoAccumulatorItemHash`](transfer::Configuration::UtxoAccumulatorItemHash) +/// in the transfer [`Configuration`](transfer::Configuration). +#[inline] +fn item_hash(parameters: &C::Parameters, utxo: &Utxo) -> UtxoAccumulatorItem +where + C: Configuration, +{ + parameters + .utxo_accumulator_item_hash() + .item_hash(utxo, &mut ()) +} + +/// Inserts the hash of `utxo` in `utxo_accumulator`. +#[allow(clippy::too_many_arguments)] +#[inline] +fn insert_next_item( + authorization_context: &mut AuthorizationContext, + utxo_accumulator: &mut C::UtxoAccumulator, + assets: &mut C::AssetMap, + parameters: &Parameters, + utxo: Utxo, + identified_asset: IdentifiedAsset, + nullifiers: &mut Vec>, + deposit: &mut Vec>, + rng: &mut C::Rng, +) where + C: Configuration, +{ + let IdentifiedAsset:: { identifier, asset } = identified_asset; + let (_, computed_utxo, nullifier) = parameters.derive_spend( + authorization_context, + identifier.clone(), + asset.clone(), + rng, + ); + if computed_utxo.is_related(&utxo) { + if let Some(index) = nullifiers + .iter() + .position(move |n| n.is_related(&nullifier)) + { + nullifiers.remove(index); + } else { + utxo_accumulator.insert(&item_hash::(parameters, &utxo)); + if !asset.is_zero() { + deposit.push(asset.clone()); + } + assets.insert(identifier, asset); + return; + } + } + utxo_accumulator.insert_nonprovable(&item_hash::(parameters, &utxo)); +} + +/// Checks if `asset` matches with `nullifier`, removing it from the `utxo_accumulator` and +/// inserting it into the `withdraw` set if this is the case. +#[allow(clippy::too_many_arguments)] +#[inline] +fn is_asset_unspent( + authorization_context: &mut AuthorizationContext, + utxo_accumulator: &mut C::UtxoAccumulator, + parameters: &Parameters, + identifier: Identifier, + asset: Asset, + nullifiers: &mut Vec>, + withdraw: &mut Vec>, + rng: &mut C::Rng, +) -> bool +where + C: Configuration, +{ + let (_, utxo, nullifier) = + parameters.derive_spend(authorization_context, identifier, asset.clone(), rng); + if let Some(index) = nullifiers + .iter() + .position(move |n| n.is_related(&nullifier)) + { + nullifiers.remove(index); + utxo_accumulator.remove_proof(&item_hash::(parameters, &utxo)); + if !asset.is_zero() { + withdraw.push(asset); + } + false + } else { + true + } +} + +/// Updates the internal ledger state, returning the new asset distribution. +#[allow(clippy::too_many_arguments)] +#[inline] +fn sync_with( + accounts: &AccountTable, + assets: &mut C::AssetMap, + checkpoint: &mut C::Checkpoint, + utxo_accumulator: &mut C::UtxoAccumulator, + parameters: &Parameters, + inserts: I, + mut nullifiers: Vec>, + is_partial: bool, + rng: &mut C::Rng, +) -> SyncResponse +where + C: Configuration, + I: Iterator, Note)>, +{ + let nullifier_count = nullifiers.len(); + let mut deposit = Vec::new(); + let mut withdraw = Vec::new(); + let mut authorization_context = default_authorization_context::(accounts, parameters); + let decryption_key = parameters.derive_decryption_key(&mut authorization_context); + for (utxo, note) in inserts { + if let Some((identifier, asset)) = parameters.open_with_check(&decryption_key, &utxo, note) + { + insert_next_item::( + &mut authorization_context, + utxo_accumulator, + assets, + parameters, + utxo, + transfer::utxo::IdentifiedAsset::new(identifier, asset), + &mut nullifiers, + &mut deposit, + rng, + ); + } else { + utxo_accumulator.insert_nonprovable(&item_hash::(parameters, &utxo)); + } + } + assets.retain(|identifier, assets| { + assets.retain(|asset| { + is_asset_unspent::( + &mut authorization_context, + utxo_accumulator, + parameters, + identifier.clone(), + asset.clone(), + &mut nullifiers, + &mut withdraw, + rng, + ) + }); + !assets.is_empty() + }); + checkpoint.update_from_nullifiers(nullifier_count); + checkpoint.update_from_utxo_accumulator(utxo_accumulator); + SyncResponse { + checkpoint: checkpoint.clone(), + balance_update: if is_partial { + // TODO: Whenever we are doing a full update, don't even build the `deposit` and + // `withdraw` vectors, since we won't be needing them. + BalanceUpdate::Partial { deposit, withdraw } + } else { + BalanceUpdate::Full { + assets: assets.assets().into(), + } + }, + } +} + +/// Builds the [`PreSender`] associated to `identifier` and `asset`. +#[inline] +fn build_pre_sender( + accounts: &AccountTable, + parameters: &Parameters, + identifier: Identifier, + asset: Asset, + rng: &mut C::Rng, +) -> PreSender +where + C: Configuration, +{ + PreSender::::sample( + parameters, + &mut default_authorization_context::(accounts, parameters), + identifier, + asset, + rng, + ) +} + +/// Builds the [`Receiver`] associated with `address` and `asset`. +#[inline] +fn receiver( + parameters: &Parameters, + address: Address, + asset: Asset, + associated_data: AssociatedData, + rng: &mut C::Rng, +) -> Receiver +where + C: Configuration, +{ + Receiver::::sample(parameters, address, asset, associated_data, rng) +} + +/// Builds the [`Receiver`] associated with the default address and `asset`. +#[inline] +fn default_receiver( + accounts: &AccountTable, + parameters: &Parameters, + asset: Asset, + rng: &mut C::Rng, +) -> Receiver +where + C: Configuration, +{ + let default_address = default_address::(accounts, parameters); + receiver::(parameters, default_address, asset, Default::default(), rng) +} + +/// Selects the pre-senders which collectively own at least `asset`, returning any change. +#[inline] +fn select( + accounts: &AccountTable, + assets: &C::AssetMap, + parameters: &Parameters, + asset: &Asset, + rng: &mut C::Rng, +) -> Result, SignError> +where + C: Configuration, +{ + let selection = assets.select(asset); + if !asset.is_zero() && selection.is_empty() { + return Err(SignError::InsufficientBalance(asset.clone())); + } + Selection::new(selection, move |k, v| { + Ok(build_pre_sender::( + accounts, + parameters, + k, + Asset::::new(asset.id.clone(), v), + rng, + )) + }) +} + +/// Builds a [`TransferPost`] for the given `transfer`. +#[inline] +fn build_post_inner< + C, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, +>( + parameters: FullParametersRef, + proving_context: &ProvingContext, + spending_key: Option<&SpendingKey>, + transfer: Transfer, + rng: &mut C::Rng, +) -> Result, SignError> +where + C: Configuration, +{ + transfer + .into_post(parameters, proving_context, spending_key, rng) + .map(|p| p.expect("Internally, all transfer posts are constructed correctly.")) + .map_err(SignError::ProofSystemError) +} + +/// Builds a [`TransferPost`] for the given `transfer`. +#[inline] +fn build_post< + C, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, +>( + accounts: &AccountTable, + utxo_accumulator_model: &UtxoAccumulatorModel, + parameters: &Parameters, + proving_context: &ProvingContext, + transfer: Transfer, + rng: &mut C::Rng, +) -> Result, SignError> +where + C: Configuration, +{ + let spending_key = default_spending_key::(accounts, parameters); + build_post_inner( + FullParametersRef::::new(parameters, utxo_accumulator_model), + proving_context, + requires_authorization(SENDERS).then_some(&spending_key), + transfer, + rng, + ) +} + +/// Computes the next [`Join`](Join) element for an asset rebalancing round. +#[allow(clippy::type_complexity)] // NOTE: Clippy is too harsh here. +#[inline] +fn next_join( + accounts: &AccountTable, + parameters: &Parameters, + asset_id: &C::AssetId, + total: C::AssetValue, + rng: &mut C::Rng, +) -> Result<([Receiver; PrivateTransferShape::RECEIVERS], Join), SignError> +where + C: Configuration, +{ + Ok(Join::new( + parameters, + &mut default_authorization_context::(accounts, parameters), + default_address::(accounts, parameters), + Asset::::new(asset_id.clone(), total), + rng, + )) +} + +/// Prepares the final pre-senders for the last part of the transaction. +#[allow(clippy::too_many_arguments)] +#[inline] +fn prepare_final_pre_senders( + accounts: &AccountTable, + assets: &C::AssetMap, + utxo_accumulator: &C::UtxoAccumulator, + parameters: &Parameters, + asset_id: &C::AssetId, + mut new_zeroes: Vec>, + pre_senders: Vec>, + rng: &mut C::Rng, +) -> Result>, SignError> +where + C: Configuration, +{ + let mut senders = pre_senders + .into_iter() + .map(|s| s.try_upgrade(parameters, utxo_accumulator)) + .collect::>>() + .expect("Unable to upgrade expected UTXOs."); + let mut needed_zeroes = PrivateTransferShape::SENDERS - senders.len(); + if needed_zeroes == 0 { + return Ok(senders); + } + let zeroes = assets.zeroes(needed_zeroes, asset_id); + needed_zeroes -= zeroes.len(); + for zero in zeroes { + let pre_sender = build_pre_sender::( + accounts, + parameters, + zero, + Asset::::new(asset_id.clone(), Default::default()), + rng, + ); + senders.push( + pre_sender + .try_upgrade(parameters, utxo_accumulator) + .expect("Unable to upgrade expected UTXOs."), + ); + } + if needed_zeroes == 0 { + return Ok(senders); + } + let needed_fake_zeroes = needed_zeroes.saturating_sub(new_zeroes.len()); + for _ in 0..needed_zeroes { + match new_zeroes.pop() { + Some(zero) => senders.push( + zero.try_upgrade(parameters, utxo_accumulator) + .expect("Unable to upgrade expected UTXOs."), + ), + _ => break, + } + } + if needed_fake_zeroes == 0 { + return Ok(senders); + } + for _ in 0..needed_fake_zeroes { + let identifier = rng.gen(); + senders.push( + build_pre_sender::( + accounts, + parameters, + identifier, + Asset::::new(asset_id.clone(), Default::default()), + rng, + ) + .upgrade_unchecked(Default::default()), + ); + } + Ok(senders) +} + +/// Builds two virtual [`Sender`]s for `pre_sender`. +#[inline] +fn virtual_senders( + accounts: &AccountTable, + utxo_accumulator_model: &UtxoAccumulatorModel, + parameters: &Parameters, + asset_id: &C::AssetId, + pre_sender: PreSender, + rng: &mut C::Rng, +) -> Result<[Sender; PrivateTransferShape::SENDERS], SignError> +where + C: Configuration, +{ + let mut utxo_accumulator = C::UtxoAccumulator::empty(utxo_accumulator_model); + let sender = pre_sender + .insert_and_upgrade(parameters, &mut utxo_accumulator) + .expect("Unable to upgrade expected UTXO."); + let mut senders = Vec::new(); + senders.push(sender); + let identifier = rng.gen(); + senders.push( + build_pre_sender::( + accounts, + parameters, + identifier, + Asset::::new(asset_id.clone(), Default::default()), + rng, + ) + .upgrade_unchecked(Default::default()), + ); + Ok(into_array_unchecked(senders)) +} + +/// Computes the batched transactions for rebalancing before a final transfer. +#[allow(clippy::too_many_arguments)] +#[inline] +fn compute_batched_transactions( + accounts: &AccountTable, + assets: &C::AssetMap, + utxo_accumulator: &mut C::UtxoAccumulator, + parameters: &Parameters, + proving_context: &MultiProvingContext, + asset_id: &C::AssetId, + mut pre_senders: Vec>, + posts: &mut Vec>, + rng: &mut C::Rng, +) -> Result<[Sender; PrivateTransferShape::SENDERS], SignError> +where + C: Configuration, +{ + let mut new_zeroes = Vec::new(); + while pre_senders.len() > PrivateTransferShape::SENDERS { + let mut joins = Vec::new(); + let mut iter = pre_senders + .into_iter() + .chunk_by::<{ PrivateTransferShape::SENDERS }>(); + for chunk in &mut iter { + let senders = array_map(chunk, |s| { + s.try_upgrade(parameters, utxo_accumulator) + .expect("Unable to upgrade expected UTXO.") + }); + let (receivers, mut join) = next_join( + accounts, + parameters, + asset_id, + senders.iter().map(|s| s.asset().value).sum(), + rng, + )?; + let authorization = + authorization_for_default_spending_key::(accounts, parameters, rng); + posts.push(build_post( + accounts, + utxo_accumulator.model(), + parameters, + &proving_context.private_transfer, + PrivateTransfer::build(authorization, senders, receivers), + rng, + )?); + join.insert_utxos(parameters, utxo_accumulator); + joins.push(join.pre_sender); + new_zeroes.append(&mut join.zeroes); + } + joins.append(&mut iter.remainder()); + pre_senders = joins; + } + let final_presenders = prepare_final_pre_senders( + accounts, + assets, + utxo_accumulator, + parameters, + asset_id, + new_zeroes, + pre_senders, + rng, + )?; + Ok(into_array_unchecked(final_presenders)) +} + +/// Returns the [`Address`] corresponding to `accounts`. +#[inline] +pub fn address(parameters: &SignerParameters, accounts: &AccountTable) -> Address +where + C: Configuration, +{ + let account = default_account::(accounts); + account.address(¶meters.parameters) +} + +/// Updates `assets`, `checkpoint` and `utxo_accumulator`, returning the new asset distribution. +#[inline] +pub fn sync( + parameters: &SignerParameters, + accounts: &AccountTable, + assets: &mut C::AssetMap, + checkpoint: &mut C::Checkpoint, + utxo_accumulator: &mut C::UtxoAccumulator, + mut request: SyncRequest, + rng: &mut C::Rng, +) -> Result, SyncError> +where + C: Configuration, +{ + // TODO: Do a capacity check on the current UTXO accumulator? + // + // if utxo_accumulator.capacity() < starting_index { + // panic!("full capacity") + // } + if checkpoint < &mut request.origin_checkpoint { + Err(SyncError::InconsistentSynchronization { + checkpoint: checkpoint.clone(), + }) + } else { + let has_pruned = request.prune( + parameters.parameters.utxo_accumulator_item_hash(), + checkpoint, + ); + let SyncData { + utxo_note_data, + nullifier_data, + } = request.data; + let response = sync_with::( + accounts, + assets, + checkpoint, + utxo_accumulator, + ¶meters.parameters, + utxo_note_data.into_iter(), + nullifier_data, + !has_pruned, + rng, + ); + utxo_accumulator.commit(); + Ok(response) + } +} + +/// Signs a withdraw transaction for `asset` sent to `address`. +#[inline] +fn sign_withdraw( + parameters: &SignerParameters, + accounts: &AccountTable, + assets: &C::AssetMap, + utxo_accumulator: &mut C::UtxoAccumulator, + asset: Asset, + address: Option>, + rng: &mut C::Rng, +) -> Result, SignError> +where + C: Configuration, +{ + let selection = select(accounts, assets, ¶meters.parameters, &asset, rng)?; + let mut posts = Vec::new(); + let senders = compute_batched_transactions( + accounts, + assets, + utxo_accumulator, + ¶meters.parameters, + ¶meters.proving_context, + &asset.id, + selection.pre_senders, + &mut posts, + rng, + )?; + let change = default_receiver::( + accounts, + ¶meters.parameters, + Asset::::new(asset.id.clone(), selection.change), + rng, + ); + let authorization = + authorization_for_default_spending_key::(accounts, ¶meters.parameters, rng); + let final_post = match address { + Some(address) => { + let receiver = receiver::( + ¶meters.parameters, + address, + asset, + Default::default(), + rng, + ); + build_post( + accounts, + utxo_accumulator.model(), + ¶meters.parameters, + ¶meters.proving_context.private_transfer, + PrivateTransfer::build(authorization, senders, [change, receiver]), + rng, + )? + } + _ => build_post( + accounts, + utxo_accumulator.model(), + ¶meters.parameters, + ¶meters.proving_context.to_public, + ToPublic::build(authorization, senders, [change], asset), + rng, + )?, + }; + posts.push(final_post); + Ok(SignResponse::new(posts)) +} + +/// Signs the `transaction`, generating transfer posts without releasing resources. +#[inline] +fn sign_internal( + parameters: &SignerParameters, + accounts: &AccountTable, + assets: &C::AssetMap, + utxo_accumulator: &mut C::UtxoAccumulator, + transaction: Transaction, + rng: &mut C::Rng, +) -> Result, SignError> +where + C: Configuration, +{ + match transaction { + Transaction::ToPrivate(asset) => { + let receiver = + default_receiver::(accounts, ¶meters.parameters, asset.clone(), rng); + Ok(SignResponse::new(vec![build_post( + accounts, + utxo_accumulator.model(), + ¶meters.parameters, + ¶meters.proving_context.to_private, + ToPrivate::build(asset, receiver), + rng, + )?])) + } + Transaction::PrivateTransfer(asset, address) => sign_withdraw( + parameters, + accounts, + assets, + utxo_accumulator, + asset, + Some(address), + rng, + ), + Transaction::ToPublic(asset) => sign_withdraw( + parameters, + accounts, + assets, + utxo_accumulator, + asset, + None, + rng, + ), + } +} + +/// Signs the `transaction`, generating transfer posts. +#[inline] +pub fn sign( + parameters: &SignerParameters, + accounts: &AccountTable, + assets: &C::AssetMap, + utxo_accumulator: &mut C::UtxoAccumulator, + transaction: Transaction, + rng: &mut C::Rng, +) -> Result, SignError> +where + C: Configuration, +{ + let result = sign_internal( + parameters, + accounts, + assets, + utxo_accumulator, + transaction, + rng, + )?; + utxo_accumulator.rollback(); + Ok(result) +} + +/// Generates an [`IdentityProof`] for `identified_asset` by +/// signing a virtual [`ToPublic`] transaction. +#[inline] +pub fn identity_proof( + parameters: &SignerParameters, + accounts: &AccountTable, + utxo_accumulator_model: &UtxoAccumulatorModel, + identified_asset: IdentifiedAsset, + rng: &mut C::Rng, +) -> Option> +where + C: Configuration, +{ + let presender = build_pre_sender::( + accounts, + ¶meters.parameters, + identified_asset.identifier, + identified_asset.asset.clone(), + rng, + ); + let senders = virtual_senders::( + accounts, + utxo_accumulator_model, + ¶meters.parameters, + &identified_asset.asset.id, + presender, + rng, + ) + .ok()?; + let change = default_receiver::( + accounts, + ¶meters.parameters, + Asset::::new(identified_asset.asset.id.clone(), Default::default()), + rng, + ); + let authorization = + authorization_for_default_spending_key::(accounts, ¶meters.parameters, rng); + let transfer_post = build_post( + accounts, + utxo_accumulator_model, + ¶meters.parameters, + ¶meters.proving_context.to_public, + ToPublic::build(authorization, senders, [change], identified_asset.asset), + rng, + ) + .ok()?; + Some(IdentityProof { transfer_post }) +} + +/// Returns the associated [`TransactionData`] of `post`, namely the [`Asset`] and the +/// [`Identifier`]. Returns `None` if `post` has an invalid shape, or if `accounts` doesn't own the +/// underlying assets in `post`. +#[inline] +pub fn transaction_data( + parameters: &SignerParameters, + accounts: &AccountTable, + post: TransferPost, +) -> Option> +where + C: Configuration, +{ + let shape = TransferShape::from_post(&post)?; + let parameters = ¶meters.parameters; + let mut authorization_context = default_authorization_context::(accounts, parameters); + let decryption_key = parameters.derive_decryption_key(&mut authorization_context); + match shape { + TransferShape::ToPrivate => { + let ReceiverPost { utxo, note } = post.body.receiver_posts.take_first(); + let (identifier, asset) = parameters.open_with_check(&decryption_key, &utxo, note)?; + Some(TransactionData::::ToPrivate(identifier, asset)) + } + TransferShape::PrivateTransfer => { + let mut transaction_data = Vec::new(); + let receiver_posts = post.body.receiver_posts; + for receiver_post in receiver_posts.into_iter() { + let ReceiverPost { utxo, note } = receiver_post; + if let Some(identified_asset) = + parameters.open_with_check(&decryption_key, &utxo, note) + { + transaction_data.push(identified_asset); + } + } + if transaction_data.is_empty() { + None + } else { + Some(TransactionData::::PrivateTransfer(transaction_data)) + } + } + TransferShape::ToPublic => { + let ReceiverPost { utxo, note } = post.body.receiver_posts.take_first(); + let (identifier, asset) = parameters.open_with_check(&decryption_key, &utxo, note)?; + Some(TransactionData::::ToPublic(identifier, asset)) + } + } +} + +/// Signs the `transaction`, generating transfer posts +/// and returning their [`TransactionData`]. +#[inline] +pub fn sign_with_transaction_data( + parameters: &SignerParameters, + accounts: &AccountTable, + assets: &C::AssetMap, + utxo_accumulator: &mut C::UtxoAccumulator, + transaction: Transaction, + rng: &mut C::Rng, +) -> SignWithTransactionDataResult +where + C: Configuration, + TransferPost: Clone, +{ + Ok(SignWithTransactionDataResponse( + sign( + parameters, + accounts, + assets, + utxo_accumulator, + transaction, + rng, + )? + .posts + .into_iter() + .map(|post| { + (post.clone(), transaction_data(parameters, accounts, post) + .expect("Retrieving transaction data from your own TransferPosts is not allowed to fail")) + }) + .collect(), + )) +} diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer/mod.rs similarity index 56% rename from manta-accounting/src/wallet/signer.rs rename to manta-accounting/src/wallet/signer/mod.rs index 663d63e5e..aabdaae55 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer/mod.rs @@ -27,38 +27,29 @@ use crate::{ asset::AssetMap, - key::{self, Account, AccountCollection, DeriveAddress, DeriveAddresses}, + key::{self, Account, AccountCollection, DeriveAddresses}, transfer::{ self, - batch::Join, - canonical::{ - MultiProvingContext, PrivateTransfer, PrivateTransferShape, Selection, ToPrivate, - ToPublic, Transaction, TransactionData, TransferShape, - }, - receiver::ReceiverPost, - requires_authorization, - utxo::{auth::DeriveContext, DeriveDecryptionKey, DeriveSpend, Spend, UtxoReconstruct}, - Address, Asset, AssociatedData, Authorization, AuthorizationContext, FullParametersRef, - IdentifiedAsset, Identifier, IdentityProof, Note, Nullifier, Parameters, PreSender, - ProofSystemError, ProvingContext, Receiver, Sender, Shape, SpendingKey, Transfer, - TransferPost, Utxo, UtxoAccumulatorItem, UtxoAccumulatorModel, UtxoMembershipProof, + canonical::{MultiProvingContext, Transaction, TransactionData}, + Address, Asset, IdentifiedAsset, Identifier, IdentityProof, Note, Nullifier, Parameters, + ProofSystemError, SpendingKey, TransferPost, Utxo, UtxoAccumulatorItem, + UtxoAccumulatorModel, UtxoMembershipProof, }, wallet::ledger::{self, Data}, }; -use alloc::{boxed::Box, vec, vec::Vec}; +use alloc::{boxed::Box, vec::Vec}; use core::{convert::Infallible, fmt::Debug, hash::Hash}; use manta_crypto::{ accumulator::{Accumulator, ExactSizeAccumulator, ItemHashFunction, OptimizedAccumulator}, - rand::{CryptoRng, FromEntropy, Rand, RngCore}, -}; -use manta_util::{ - array_map, cmp::Independence, future::LocalBoxFutureResult, into_array_unchecked, - iter::IteratorExt, persistence::Rollback, vec::VecExt, + rand::{CryptoRng, FromEntropy, RngCore}, }; +use manta_util::{future::LocalBoxFutureResult, persistence::Rollback}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; +pub mod functions; + /// Signer Connection pub trait Connection where @@ -89,7 +80,7 @@ where fn sign( &mut self, request: SignRequest, - ) -> LocalBoxFutureResult, SignError>, Self::Error>; + ) -> LocalBoxFutureResult, Self::Error>; /// Returns the [`Address`] corresponding to `self`. fn address(&mut self) -> LocalBoxFutureResult, Self::Error>; @@ -106,6 +97,15 @@ where &mut self, request: IdentityRequest, ) -> LocalBoxFutureResult, Self::Error>; + + /// Signs a transaction and returns the ledger transfer posts and the + /// associated [`TransactionData`] if successful. + fn sign_with_transaction_data( + &mut self, + request: SignRequest, + ) -> LocalBoxFutureResult, Self::Error> + where + TransferPost: Clone; } /// Signer Synchronization Data @@ -317,6 +317,31 @@ pub struct TransactionDataResponse(pub Vec>>) where C: transfer::Configuration; +/// Sign with Transaction Data Response +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "TransferPost: Deserialize<'de>, TransactionData: Deserialize<'de>", + serialize = "TransferPost: Serialize, TransactionData: Serialize", + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "TransferPost: Clone, TransactionData: Clone"), + Debug(bound = "TransferPost: Debug, TransactionData: Debug"), + Eq(bound = "TransferPost: Eq, TransactionData: Eq"), + Hash(bound = "TransferPost: Hash, TransactionData: Hash"), + PartialEq(bound = "TransferPost: PartialEq, TransactionData: PartialEq") +)] +pub struct SignWithTransactionDataResponse(pub Vec<(TransferPost, TransactionData)>) +where + C: transfer::Configuration; + /// Balance Update #[cfg_attr( feature = "serde", @@ -511,6 +536,16 @@ pub struct IdentityResponse(pub Vec>>) where C: transfer::Configuration; +impl IdentityResponse +where + C: transfer::Configuration, +{ + /// Builds a new [`IdentityResponse`] from a vector of [`IdentityProof`]s. + pub fn new(identity_proofs: Vec>>) -> Self { + Self(identity_proofs) + } +} + impl SignResponse where C: transfer::Configuration, @@ -561,6 +596,10 @@ where /// Signing Result pub type SignResult = Result, SignError>; +/// Signing with Transaction Data Error +pub type SignWithTransactionDataResult = + Result, SignError>; + /// Signer Checkpoint pub trait Checkpoint: ledger::Checkpoint where @@ -813,439 +852,6 @@ where pub fn default_account(&self) -> Account { self.accounts.get_default() } - - /// Returns the default spending key for `self`. - #[inline] - fn default_spending_key(&self, parameters: &C::Parameters) -> SpendingKey { - let _ = parameters; - self.accounts.get_default().spending_key() - } - - /// Returns the default authorization context for `self`. - #[inline] - fn default_authorization_context(&self, parameters: &C::Parameters) -> AuthorizationContext { - parameters.derive_context(&self.default_spending_key(parameters)) - } - - /// Returns the authorization for the default spending key of `self`. - #[inline] - fn authorization_for_default_spending_key( - &mut self, - parameters: &C::Parameters, - ) -> Authorization { - Authorization::::from_spending_key( - parameters, - &self.default_spending_key(parameters), - &mut self.rng, - ) - } - - /// Returns the address for the default account of `self`. - #[inline] - fn default_address(&mut self, parameters: &C::Parameters) -> Address { - self.accounts.get_default().address(parameters) - } - - /// Hashes `utxo` using the [`UtxoAccumulatorItemHash`](transfer::Configuration::UtxoAccumulatorItemHash) - /// in the transfer [`Configuration`](transfer::Configuration). - #[inline] - fn item_hash(parameters: &C::Parameters, utxo: &Utxo) -> UtxoAccumulatorItem { - parameters - .utxo_accumulator_item_hash() - .item_hash(utxo, &mut ()) - } - - /// Inserts the hash of `utxo` in `utxo_accumulator`. - #[allow(clippy::too_many_arguments)] // FIXME: Use a better abstraction here. - #[inline] - fn insert_next_item( - authorization_context: &mut AuthorizationContext, - utxo_accumulator: &mut C::UtxoAccumulator, - assets: &mut C::AssetMap, - parameters: &Parameters, - utxo: Utxo, - identified_asset: IdentifiedAsset, - nullifiers: &mut Vec>, - deposit: &mut Vec>, - rng: &mut R, - ) where - R: CryptoRng + RngCore + ?Sized, - { - let IdentifiedAsset:: { identifier, asset } = identified_asset; - let (_, computed_utxo, nullifier) = parameters.derive_spend( - authorization_context, - identifier.clone(), - asset.clone(), - rng, - ); - if computed_utxo.is_related(&utxo) { - if let Some(index) = nullifiers - .iter() - .position(move |n| n.is_related(&nullifier)) - { - nullifiers.remove(index); - } else { - utxo_accumulator.insert(&Self::item_hash(parameters, &utxo)); - if !asset.is_zero() { - deposit.push(asset.clone()); - } - assets.insert(identifier, asset); - return; - } - } - utxo_accumulator.insert_nonprovable(&Self::item_hash(parameters, &utxo)); - } - - /// Checks if `asset` matches with `nullifier`, removing it from the `utxo_accumulator` and - /// inserting it into the `withdraw` set if this is the case. - #[allow(clippy::too_many_arguments)] // FIXME: Use a better abstraction here. - #[inline] - fn is_asset_unspent( - authorization_context: &mut AuthorizationContext, - utxo_accumulator: &mut C::UtxoAccumulator, - parameters: &Parameters, - identifier: Identifier, - asset: Asset, - nullifiers: &mut Vec>, - withdraw: &mut Vec>, - rng: &mut R, - ) -> bool - where - R: CryptoRng + RngCore + ?Sized, - { - let (_, utxo, nullifier) = - parameters.derive_spend(authorization_context, identifier, asset.clone(), rng); - if let Some(index) = nullifiers - .iter() - .position(move |n| n.is_related(&nullifier)) - { - nullifiers.remove(index); - utxo_accumulator.remove_proof(&Self::item_hash(parameters, &utxo)); - if !asset.is_zero() { - withdraw.push(asset); - } - false - } else { - true - } - } - - /// Updates the internal ledger state, returning the new asset distribution. - #[inline] - fn sync_with( - &mut self, - parameters: &Parameters, - inserts: I, - mut nullifiers: Vec>, - is_partial: bool, - ) -> SyncResponse - where - I: Iterator, Note)>, - { - let nullifier_count = nullifiers.len(); - let mut deposit = Vec::new(); - let mut withdraw = Vec::new(); - let mut authorization_context = self.default_authorization_context(parameters); - let decryption_key = parameters.derive_decryption_key(&mut authorization_context); - for (utxo, note) in inserts { - if let Some((identifier, asset)) = - parameters.open_with_check(&decryption_key, &utxo, note) - { - Self::insert_next_item( - &mut authorization_context, - &mut self.utxo_accumulator, - &mut self.assets, - parameters, - utxo, - transfer::utxo::IdentifiedAsset::new(identifier, asset), - &mut nullifiers, - &mut deposit, - &mut self.rng, - ); - } else { - self.utxo_accumulator - .insert_nonprovable(&Self::item_hash(parameters, &utxo)); - } - } - self.assets.retain(|identifier, assets| { - assets.retain(|asset| { - Self::is_asset_unspent( - &mut authorization_context, - &mut self.utxo_accumulator, - parameters, - identifier.clone(), - asset.clone(), - &mut nullifiers, - &mut withdraw, - &mut self.rng, - ) - }); - !assets.is_empty() - }); - self.checkpoint.update_from_nullifiers(nullifier_count); - self.checkpoint - .update_from_utxo_accumulator(&self.utxo_accumulator); - SyncResponse { - checkpoint: self.checkpoint.clone(), - balance_update: if is_partial { - // TODO: Whenever we are doing a full update, don't even build the `deposit` and - // `withdraw` vectors, since we won't be needing them. - BalanceUpdate::Partial { deposit, withdraw } - } else { - BalanceUpdate::Full { - assets: self.assets.assets().into(), - } - }, - } - } - - /// Builds the [`PreSender`] associated to `identifier` and `asset`. - #[inline] - fn build_pre_sender( - &mut self, - parameters: &Parameters, - identifier: Identifier, - asset: Asset, - ) -> PreSender { - PreSender::::sample( - parameters, - &mut self.default_authorization_context(parameters), - identifier, - asset, - &mut self.rng, - ) - } - - /// Builds the [`Receiver`] associated with `address` and `asset`. - #[inline] - fn receiver( - &mut self, - parameters: &Parameters, - address: Address, - asset: Asset, - associated_data: AssociatedData, - ) -> Receiver { - Receiver::::sample(parameters, address, asset, associated_data, &mut self.rng) - } - - /// Builds the [`Receiver`] associated with the default address and `asset`. - #[inline] - fn default_receiver(&mut self, parameters: &Parameters, asset: Asset) -> Receiver { - let default_address = self.default_address(parameters); - self.receiver(parameters, default_address, asset, Default::default()) - } - - /// Selects the pre-senders which collectively own at least `asset`, returning any change. - #[inline] - fn select( - &mut self, - parameters: &Parameters, - asset: &Asset, - ) -> Result, SignError> { - let selection = self.assets.select(asset); - if !asset.is_zero() && selection.is_empty() { - return Err(SignError::InsufficientBalance(asset.clone())); - } - Selection::new(selection, move |k, v| { - Ok(self.build_pre_sender(parameters, k, Asset::::new(asset.id.clone(), v))) - }) - } - - /// Builds a [`TransferPost`] for the given `transfer`. - #[inline] - fn build_post_inner< - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, - >( - parameters: FullParametersRef, - proving_context: &ProvingContext, - spending_key: Option<&SpendingKey>, - transfer: Transfer, - rng: &mut C::Rng, - ) -> Result, SignError> { - transfer - .into_post(parameters, proving_context, spending_key, rng) - .map(|p| p.expect("Internally, all transfer posts are constructed correctly.")) - .map_err(SignError::ProofSystemError) - } - - /// Builds a [`TransferPost`] for the given `transfer`. - #[inline] - fn build_post< - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, - >( - &mut self, - parameters: &Parameters, - proving_context: &ProvingContext, - transfer: Transfer, - ) -> Result, SignError> { - let spending_key = self.default_spending_key(parameters); - Self::build_post_inner( - FullParametersRef::::new(parameters, self.utxo_accumulator.model()), - proving_context, - requires_authorization(SENDERS).then_some(&spending_key), - transfer, - &mut self.rng, - ) - } - - /// Computes the next [`Join`](Join) element for an asset rebalancing round. - #[allow(clippy::type_complexity)] // NOTE: Clippy is too harsh here. - #[inline] - fn next_join( - &mut self, - parameters: &Parameters, - asset_id: &C::AssetId, - total: C::AssetValue, - ) -> Result<([Receiver; PrivateTransferShape::RECEIVERS], Join), SignError> { - Ok(Join::new( - parameters, - &mut self.default_authorization_context(parameters), - self.default_address(parameters), - Asset::::new(asset_id.clone(), total), - &mut self.rng, - )) - } - - /// Prepares the final pre-senders for the last part of the transaction. - #[inline] - fn prepare_final_pre_senders( - &mut self, - parameters: &Parameters, - asset_id: &C::AssetId, - mut new_zeroes: Vec>, - pre_senders: Vec>, - ) -> Result>, SignError> { - let mut senders = pre_senders - .into_iter() - .map(|s| s.try_upgrade(parameters, &self.utxo_accumulator)) - .collect::>>() - .expect("Unable to upgrade expected UTXOs."); - let mut needed_zeroes = PrivateTransferShape::SENDERS - senders.len(); - if needed_zeroes == 0 { - return Ok(senders); - } - let zeroes = self.assets.zeroes(needed_zeroes, asset_id); - needed_zeroes -= zeroes.len(); - for zero in zeroes { - let pre_sender = self.build_pre_sender( - parameters, - zero, - Asset::::new(asset_id.clone(), Default::default()), - ); - senders.push( - pre_sender - .try_upgrade(parameters, &self.utxo_accumulator) - .expect("Unable to upgrade expected UTXOs."), - ); - } - if needed_zeroes == 0 { - return Ok(senders); - } - let needed_fake_zeroes = needed_zeroes.saturating_sub(new_zeroes.len()); - for _ in 0..needed_zeroes { - match new_zeroes.pop() { - Some(zero) => senders.push( - zero.try_upgrade(parameters, &self.utxo_accumulator) - .expect("Unable to upgrade expected UTXOs."), - ), - _ => break, - } - } - if needed_fake_zeroes == 0 { - return Ok(senders); - } - for _ in 0..needed_fake_zeroes { - let identifier = self.rng.gen(); - senders.push( - self.build_pre_sender( - parameters, - identifier, - Asset::::new(asset_id.clone(), Default::default()), - ) - .upgrade_unchecked(Default::default()), - ); - } - Ok(senders) - } - - /// Builds two virtual [`Sender`]s for `pre_sender`. - #[inline] - fn virtual_senders( - &mut self, - parameters: &Parameters, - asset_id: &C::AssetId, - pre_sender: PreSender, - ) -> Result<[Sender; PrivateTransferShape::SENDERS], SignError> { - let mut utxo_accumulator = C::UtxoAccumulator::empty(self.utxo_accumulator.model()); - let sender = pre_sender - .insert_and_upgrade(parameters, &mut utxo_accumulator) - .expect("Unable to upgrade expected UTXO."); - let mut senders = Vec::new(); - senders.push(sender); - let identifier = self.rng.gen(); - senders.push( - self.build_pre_sender( - parameters, - identifier, - Asset::::new(asset_id.clone(), Default::default()), - ) - .upgrade_unchecked(Default::default()), - ); - Ok(into_array_unchecked(senders)) - } - - /// Computes the batched transactions for rebalancing before a final transfer. - #[inline] - fn compute_batched_transactions( - &mut self, - parameters: &Parameters, - proving_context: &MultiProvingContext, - asset_id: &C::AssetId, - mut pre_senders: Vec>, - posts: &mut Vec>, - ) -> Result<[Sender; PrivateTransferShape::SENDERS], SignError> { - let mut new_zeroes = Vec::new(); - while pre_senders.len() > PrivateTransferShape::SENDERS { - let mut joins = Vec::new(); - let mut iter = pre_senders - .into_iter() - .chunk_by::<{ PrivateTransferShape::SENDERS }>(); - for chunk in &mut iter { - let senders = array_map(chunk, |s| { - s.try_upgrade(parameters, &self.utxo_accumulator) - .expect("Unable to upgrade expected UTXO.") - }); - let (receivers, mut join) = self.next_join( - parameters, - asset_id, - senders.iter().map(|s| s.asset().value).sum(), - )?; - let authorization = self.authorization_for_default_spending_key(parameters); - posts.push(self.build_post( - parameters, - &proving_context.private_transfer, - PrivateTransfer::build(authorization, senders, receivers), - )?); - join.insert_utxos(parameters, &mut self.utxo_accumulator); - joins.push(join.pre_sender); - new_zeroes.append(&mut join.zeroes); - } - joins.append(&mut iter.remainder()); - pre_senders = joins; - } - Ok(into_array_unchecked(self.prepare_final_pre_senders( - parameters, - asset_id, - new_zeroes, - pre_senders, - )?)) - } } impl Clone for SignerState @@ -1367,153 +973,46 @@ where #[inline] pub fn sync( &mut self, - mut request: SyncRequest, + request: SyncRequest, ) -> Result, SyncError> { - // TODO: Do a capacity check on the current UTXO accumulator? - // - // if self.utxo_accumulator.capacity() < starting_index { - // panic!("full capacity") - // } - let checkpoint = &self.state.checkpoint; - if checkpoint < &request.origin_checkpoint { - Err(SyncError::InconsistentSynchronization { - checkpoint: checkpoint.clone(), - }) - } else { - let has_pruned = request.prune( - self.parameters.parameters.utxo_accumulator_item_hash(), - checkpoint, - ); - let SyncData { - utxo_note_data, - nullifier_data, - } = request.data; - let response = self.state.sync_with( - &self.parameters.parameters, - utxo_note_data.into_iter(), - nullifier_data, - !has_pruned, - ); - self.state.utxo_accumulator.commit(); - Ok(response) - } - } - - /// Signs a withdraw transaction for `asset` sent to `address`. - #[inline] - fn sign_withdraw( - &mut self, - asset: Asset, - address: Option>, - ) -> Result, SignError> { - let selection = self.state.select(&self.parameters.parameters, &asset)?; - let mut posts = Vec::new(); - let senders = self.state.compute_batched_transactions( - &self.parameters.parameters, - &self.parameters.proving_context, - &asset.id, - selection.pre_senders, - &mut posts, - )?; - let change = self.state.default_receiver( - &self.parameters.parameters, - Asset::::new(asset.id.clone(), selection.change), - ); - let authorization = self - .state - .authorization_for_default_spending_key(&self.parameters.parameters); - let final_post = match address { - Some(address) => { - let receiver = self.state.receiver( - &self.parameters.parameters, - address, - asset, - Default::default(), - ); - self.state.build_post( - &self.parameters.parameters, - &self.parameters.proving_context.private_transfer, - PrivateTransfer::build(authorization, senders, [change, receiver]), - )? - } - _ => self.state.build_post( - &self.parameters.parameters, - &self.parameters.proving_context.to_public, - ToPublic::build(authorization, senders, [change], asset), - )?, - }; - posts.push(final_post); - Ok(SignResponse::new(posts)) + functions::sync( + &self.parameters, + &self.state.accounts, + &mut self.state.assets, + &mut self.state.checkpoint, + &mut self.state.utxo_accumulator, + request, + &mut self.state.rng, + ) } /// Generates an [`IdentityProof`] for `identified_asset` by - /// signing a virtual [`ToPublic`] transaction. + /// signing a virtual [`ToPublic`](transfer::canonical::ToPublic) transaction. #[inline] pub fn identity_proof( &mut self, identified_asset: IdentifiedAsset, ) -> Option> { - let presender = self.state.build_pre_sender( - &self.parameters.parameters, - identified_asset.identifier, - identified_asset.asset.clone(), - ); - let senders = self - .state - .virtual_senders( - &self.parameters.parameters, - &identified_asset.asset.id, - presender, - ) - .ok()?; - let change = self.state.default_receiver( - &self.parameters.parameters, - Asset::::new(identified_asset.asset.id.clone(), Default::default()), - ); - let authorization = self - .state - .authorization_for_default_spending_key(&self.parameters.parameters); - let transfer_post = self - .state - .build_post( - &self.parameters.parameters, - &self.parameters.proving_context.to_public, - ToPublic::build(authorization, senders, [change], identified_asset.asset), - ) - .ok()?; - Some(IdentityProof { transfer_post }) - } - - /// Signs the `transaction`, generating transfer posts without releasing resources. - #[inline] - fn sign_internal( - &mut self, - transaction: Transaction, - ) -> Result, SignError> { - match transaction { - Transaction::ToPrivate(asset) => { - let receiver = self - .state - .default_receiver(&self.parameters.parameters, asset.clone()); - Ok(SignResponse::new(vec![self.state.build_post( - &self.parameters.parameters, - &self.parameters.proving_context.to_private, - ToPrivate::build(asset, receiver), - )?])) - } - Transaction::PrivateTransfer(asset, address) => { - self.sign_withdraw(asset, Some(address)) - } - Transaction::ToPublic(asset) => self.sign_withdraw(asset, None), - } + functions::identity_proof( + &self.parameters, + &self.state.accounts, + self.state.utxo_accumulator.model(), + identified_asset, + &mut self.state.rng, + ) } /// Signs the `transaction`, generating transfer posts. #[inline] pub fn sign(&mut self, transaction: Transaction) -> Result, SignError> { - let result = self.sign_internal(transaction); - self.state.utxo_accumulator.rollback(); - result + functions::sign( + &self.parameters, + &self.state.accounts, + &self.state.assets, + &mut self.state.utxo_accumulator, + transaction, + &mut self.state.rng, + ) } /// Returns a vector with the [`IdentityProof`] corresponding to each [`IdentifiedAsset`] in `identified_assets`. @@ -1533,8 +1032,7 @@ where /// Returns the [`Address`] corresponding to `self`. #[inline] pub fn address(&mut self) -> Address { - let account = self.state.accounts.get_default(); - account.address(&self.parameters.parameters) + functions::address(&self.parameters, &self.state.accounts) } /// Returns the associated [`TransactionData`] of `post`, namely the [`Asset`] and the @@ -1542,41 +1040,7 @@ where /// underlying assets in `post`. #[inline] pub fn transaction_data(&self, post: TransferPost) -> Option> { - let shape = TransferShape::from_post(&post)?; - let parameters = &self.parameters.parameters; - let mut authorization_context = self.state.default_authorization_context(parameters); - let decryption_key = parameters.derive_decryption_key(&mut authorization_context); - match shape { - TransferShape::ToPrivate => { - let ReceiverPost { utxo, note } = post.body.receiver_posts.take_first(); - let (identifier, asset) = - parameters.open_with_check(&decryption_key, &utxo, note)?; - Some(TransactionData::::ToPrivate(identifier, asset)) - } - TransferShape::PrivateTransfer => { - let mut transaction_data = Vec::new(); - let receiver_posts = post.body.receiver_posts; - for receiver_post in receiver_posts.into_iter() { - let ReceiverPost { utxo, note } = receiver_post; - if let Some(identified_asset) = - parameters.open_with_check(&decryption_key, &utxo, note) - { - transaction_data.push(identified_asset); - } - } - if transaction_data.is_empty() { - None - } else { - Some(TransactionData::::PrivateTransfer(transaction_data)) - } - } - TransferShape::ToPublic => { - let ReceiverPost { utxo, note } = post.body.receiver_posts.take_first(); - let (identifier, asset) = - parameters.open_with_check(&decryption_key, &utxo, note)?; - Some(TransactionData::::ToPublic(identifier, asset)) - } - } + functions::transaction_data(&self.parameters, &self.state.accounts, post) } /// Returns a vector with the [`TransactionData`] of each well-formed [`TransferPost`] owned by @@ -1593,6 +1057,25 @@ where .collect(), ) } + + /// Signs the `transaction`, generating transfer posts and returning their associated [`TransactionData`]. + #[inline] + pub fn sign_with_transaction_data( + &mut self, + transaction: Transaction, + ) -> Result, SignError> + where + TransferPost: Clone, + { + functions::sign_with_transaction_data( + &self.parameters, + &self.state.accounts, + &self.state.assets, + &mut self.state.utxo_accumulator, + transaction, + &mut self.state.rng, + ) + } } impl Connection for Signer @@ -1607,10 +1090,7 @@ where fn sync( &mut self, request: SyncRequest, - ) -> LocalBoxFutureResult< - Result, SyncError>, - Self::Error, - > { + ) -> LocalBoxFutureResult, Self::Error> { Box::pin(async move { Ok(self.sync(request)) }) } @@ -1618,7 +1098,7 @@ where fn sign( &mut self, request: SignRequest, - ) -> LocalBoxFutureResult, SignError>, Self::Error> { + ) -> LocalBoxFutureResult, Self::Error> { Box::pin(async move { Ok(self.sign(request.transaction)) }) } @@ -1642,4 +1122,119 @@ where ) -> LocalBoxFutureResult, Self::Error> { Box::pin(async move { Ok(self.batched_identity_proof(request.0)) }) } + + #[inline] + fn sign_with_transaction_data( + &mut self, + request: SignRequest, + ) -> LocalBoxFutureResult, Self::Error> + where + TransferPost: Clone, + { + Box::pin(async move { Ok(self.sign_with_transaction_data(request.transaction)) }) + } +} + +/// Storage State +/// +/// This struct stores the [`Checkpoint`], +/// [`UtxoAccumulator`](Configuration::UtxoAccumulator) and [`AssetMap`] of +/// a [`SignerState`]. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "C::Checkpoint: Deserialize<'de>, C::UtxoAccumulator: Deserialize<'de>, C::AssetMap: Deserialize<'de>", + serialize = "C::Checkpoint: Serialize, C::UtxoAccumulator: Serialize, C::AssetMap: Serialize", + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "C::Checkpoint: Clone, C::UtxoAccumulator: Clone, C::AssetMap: Clone"), + Debug(bound = "C::Checkpoint: Debug, C::UtxoAccumulator: Debug, C::AssetMap: Debug"), + Eq(bound = "C::Checkpoint: Eq, C::UtxoAccumulator: Eq, C::AssetMap: Eq"), + Hash(bound = "C::Checkpoint: Hash, C::UtxoAccumulator: Hash, C::AssetMap: Hash"), + PartialEq( + bound = "C::Checkpoint: PartialEq, C::UtxoAccumulator: PartialEq, C::AssetMap: PartialEq" + ) +)] +pub struct StorageState +where + C: Configuration, +{ + /// Checkpoint + checkpoint: C::Checkpoint, + + /// Utxo Accumulator + utxo_accumulator: C::UtxoAccumulator, + + /// Assets + assets: C::AssetMap, +} + +impl StorageState +where + C: Configuration, +{ + /// Builds a new [`StorageState`] with default values from `utxo_accumulator_model`. + #[inline] + pub fn new(utxo_accumulator_model: &UtxoAccumulatorModel) -> Self { + let utxo_accumulator = Accumulator::empty(utxo_accumulator_model); + Self { + checkpoint: Checkpoint::from_utxo_accumulator(&utxo_accumulator), + utxo_accumulator, + assets: Default::default(), + } + } + + /// Updates `self` from `signer` + #[inline] + pub fn update_from_signer(&mut self, signer: &Signer) + where + C::UtxoAccumulator: Clone, + C::AssetMap: Clone, + { + self.checkpoint = signer.state.checkpoint.clone(); + self.utxo_accumulator = signer.state.utxo_accumulator.clone(); + self.assets = signer.state.assets.clone(); + } + + /// Updates `signer` from `self`. + #[inline] + pub fn update_signer(&self, signer: &mut Signer) + where + C::UtxoAccumulator: Clone, + C::AssetMap: Clone, + { + signer.state.checkpoint = self.checkpoint.clone(); + signer.state.utxo_accumulator = self.utxo_accumulator.clone(); + signer.state.assets = self.assets.clone(); + } + + /// Initializes a [`Signer`] from `self`, `accounts`, `parameters` and `proving_context`. + #[inline] + pub fn initialize_signer( + &self, + accounts: AccountTable, + parameters: Parameters, + proving_context: MultiProvingContext, + ) -> Signer + where + C::UtxoAccumulator: Clone, + C::AssetMap: Clone, + { + let mut signer = Signer::new( + accounts, + parameters, + proving_context, + self.utxo_accumulator.clone(), + FromEntropy::from_entropy(), + ); + self.update_signer(&mut signer); + signer + } } diff --git a/manta-crypto/src/arkworks/groth16.rs b/manta-crypto/src/arkworks/groth16.rs index c292bc35e..97518e4fb 100644 --- a/manta-crypto/src/arkworks/groth16.rs +++ b/manta-crypto/src/arkworks/groth16.rs @@ -32,14 +32,20 @@ use crate::{ use alloc::vec::Vec; use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, ProvingKey, VerifyingKey}; use ark_snark::SNARK; -use core::marker::PhantomData; +use core::{ + hash::{Hash, Hasher}, + marker::PhantomData, +}; use manta_util::codec::{self, DecodeError}; #[cfg(feature = "scale")] use crate::crypto::ecc::arkworks::Group; #[cfg(feature = "serde")] -use manta_util::serde::{Deserialize, Serialize, Serializer}; +use { + crate::arkworks::serialize::{canonical_deserialize, canonical_serialize}, + manta_util::serde::{Deserialize, Serialize, Serializer}, +}; /// Proof System Error /// @@ -74,6 +80,18 @@ pub struct Proof( where E: PairingEngine; +impl Hash for Proof +where + E: PairingEngine, +{ + #[inline] + fn hash(&self, state: &mut H) { + self.0.a.hash(state); + self.0.b.hash(state); + self.0.c.hash(state); + } +} + #[cfg(feature = "scale")] #[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl scale_codec::Decode for Proof @@ -188,11 +206,26 @@ where } /// Proving Context +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(derivative::Derivative, CanonicalSerialize, CanonicalDeserialize)] #[derivative(Clone, Debug, Eq, PartialEq)] -pub struct ProvingContext(pub ProvingKey) +pub struct ProvingContext where - E: PairingEngine; + E: PairingEngine, +{ + #[cfg_attr( + feature = "serde", + serde( + serialize_with = "canonical_serialize::, _>", + deserialize_with = "canonical_deserialize::<'de, _, ProvingKey>" + ) + )] + proving_key: ProvingKey, +} impl ProvingContext where @@ -201,7 +234,7 @@ where /// Builds a new [`ProvingContext`] from `proving_key`. #[inline] pub fn new(proving_key: ProvingKey) -> Self { - Self(proving_key) + Self { proving_key } } /// Returns the [`VerifyingContext`] for `self`. @@ -211,6 +244,27 @@ where } } +impl Hash for ProvingContext +where + E: PairingEngine, +{ + #[inline] + fn hash(&self, state: &mut H) { + self.proving_key.vk.alpha_g1.hash(state); + self.proving_key.vk.beta_g2.hash(state); + self.proving_key.vk.gamma_g2.hash(state); + self.proving_key.vk.delta_g2.hash(state); + self.proving_key.vk.gamma_abc_g1.hash(state); + self.proving_key.beta_g1.hash(state); + self.proving_key.delta_g1.hash(state); + self.proving_key.a_query.hash(state); + self.proving_key.b_g1_query.hash(state); + self.proving_key.b_g2_query.hash(state); + self.proving_key.h_query.hash(state); + self.proving_key.l_query.hash(state); + } +} + impl codec::Decode for ProvingContext where E: PairingEngine, @@ -226,7 +280,7 @@ where match CanonicalDeserialize::deserialize_unchecked(&mut reader) { Ok(value) => reader .finish() - .map(move |_| Self(value)) + .map(move |_| Self::new(value)) .map_err(DecodeError::Read), Err(err) => Err(DecodeError::Decode(err)), } @@ -243,7 +297,7 @@ where W: codec::Write, { let mut writer = ArkWriter::new(writer); - let _ = self.0.serialize_unchecked(&mut writer); + let _ = self.proving_key.serialize_unchecked(&mut writer); writer.finish().map(move |_| ()) } } @@ -276,7 +330,7 @@ where pub fn from_proving_context( proving_context: &ProvingContext, ) -> Result { - Self::new(&proving_context.0.vk) + Self::new(&proving_context.proving_key.vk) } } @@ -526,7 +580,7 @@ where let (proving_key, verifying_key) = ArkGroth16::circuit_specific_setup(compiler, &mut SizedRng(rng)).map_err(|_| Error)?; Ok(( - ProvingContext(proving_key), + ProvingContext::new(proving_key), VerifyingContext(ArkGroth16::process_vk(&verifying_key).map_err(|_| Error)?), )) } @@ -540,7 +594,7 @@ where where R: CryptoRng + RngCore + ?Sized, { - ArkGroth16::prove(&context.0, compiler, &mut SizedRng(rng)) + ArkGroth16::prove(&context.proving_key, compiler, &mut SizedRng(rng)) .map(Proof) .map_err(|_| Error) } diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index c4d5fc021..8e6865e2e 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -264,7 +264,10 @@ impl Default for BaseContribution { #[derivative( Clone(bound = "LeafDigest: Clone, InnerDigest: Clone, M: Clone"), Debug(bound = "LeafDigest: Debug, InnerDigest: Debug, M: Debug"), - Default(bound = "LeafDigest: Default, InnerDigest: Default") + Default(bound = "LeafDigest: Default, InnerDigest: Default"), + Eq(bound = "LeafDigest: Eq, InnerDigest: Eq, M: Eq"), + Hash(bound = "LeafDigest: Hash, InnerDigest: Hash, M: Hash"), + PartialEq(bound = "LeafDigest: PartialEq, InnerDigest: PartialEq, M: PartialEq") )] struct Branch> where @@ -783,7 +786,12 @@ where #[derive(derivative::Derivative)] #[derivative( Clone(bound = "T: Clone, LeafDigest: Clone, InnerDigest: Clone, M: Clone"), - Debug(bound = "T: Debug, LeafDigest: Debug, InnerDigest: Debug, M: Debug") + Debug(bound = "T: Debug, LeafDigest: Debug, InnerDigest: Debug, M: Debug"), + Eq(bound = "T: Eq, LeafDigest: Eq, InnerDigest: Eq, M: Eq"), + Hash(bound = "T: Hash, LeafDigest: Hash, InnerDigest: Hash, M: Hash"), + PartialEq( + bound = "T: PartialEq, LeafDigest: PartialEq, InnerDigest: PartialEq, M: PartialEq" + ) )] pub struct ForkedTree> where diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 2881afaa7..879acc519 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -50,14 +50,11 @@ download = ["manta-parameters/download", "std"] groth16 = ["manta-crypto/ark-groth16", "arkworks"] # Enable HTTP Signer Client -http = ["manta-util/reqwest", "serde", "network"] +http = ["manta-util/reqwest", "serde"] # Key Features key = ["bip32", "bip0039"] -# Enable Multiple Network Support -network = [] - # Parameter Loading parameters = ["groth16", "manta-crypto/test", "manta-parameters"] @@ -77,6 +74,7 @@ simulation = [ "manta-util/rayon", "test", "manta-util/tide", + "std", "tokio/io-std", "tokio/io-util", "tokio/macros", @@ -96,7 +94,7 @@ std = [ test = ["manta-accounting/test", "manta-crypto/test", "tempfile"] # Wallet -wallet = ["key", "manta-crypto/getrandom", "std"] +wallet = ["key", "manta-crypto/getrandom"] # Enable WebSocket Signer Client websocket = [ diff --git a/manta-pay/src/config/mod.rs b/manta-pay/src/config/mod.rs index 986dcc18a..93bb73760 100644 --- a/manta-pay/src/config/mod.rs +++ b/manta-pay/src/config/mod.rs @@ -32,7 +32,6 @@ use {alloc::string::String, manta_util::codec::Encode}; pub mod poseidon; pub mod utxo; -pub mod utxo_utilities; /// Pairing Curve Type pub type PairingCurve = Bn254; @@ -200,12 +199,18 @@ pub type SpendingKey = transfer::SpendingKey; /// Address Type pub type Address = transfer::Address; +/// Identifier Type +pub type Identifier = transfer::Identifier; + /// Identified Asset Type pub type IdentifiedAsset = transfer::IdentifiedAsset; /// Identity Type pub type IdentityProof = transfer::IdentityProof; +/// Transaction Data Type +pub type TransactionData = transfer::canonical::TransactionData; + /// Converts an [`Address`] into a base58-encoded string. #[cfg(feature = "bs58")] #[cfg_attr(doc_cfg, doc(cfg(feature = "bs58")))] diff --git a/manta-pay/src/config/utxo.rs b/manta-pay/src/config/utxo.rs index 88bd0af05..47b0f915c 100644 --- a/manta-pay/src/config/utxo.rs +++ b/manta-pay/src/config/utxo.rs @@ -268,6 +268,11 @@ impl Constant for GroupGeneratorVar { } /// Utxo Commitment Scheme Domain Tag +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct UtxoCommitmentSchemeDomainTag; @@ -292,10 +297,27 @@ impl Constant for UtxoCommitmentSchemeDomainTag { type UtxoCommitmentSchemeType = Hasher; /// Utxo Commitment Scheme +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "UtxoCommitmentSchemeType: Deserialize<'de>", + serialize = "UtxoCommitmentSchemeType: Serialize", + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "UtxoCommitmentSchemeType: Clone"), - Debug(bound = "UtxoCommitmentSchemeType: Debug") + Copy(bound = "UtxoCommitmentSchemeType: Copy"), + Debug(bound = "UtxoCommitmentSchemeType: Debug"), + Default(bound = "UtxoCommitmentSchemeType: Default"), + Eq(bound = "UtxoCommitmentSchemeType: Eq"), + Hash(bound = "UtxoCommitmentSchemeType: core::hash::Hash"), + PartialEq(bound = "UtxoCommitmentSchemeType: PartialEq") )] pub struct UtxoCommitmentScheme(UtxoCommitmentSchemeType) where @@ -401,6 +423,11 @@ impl protocol::UtxoCommitmentScheme for UtxoCommitmentScheme } /// Viewing Key Derivation Function Domain Tag +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct ViewingKeyDerivationFunctionDomainTag; @@ -426,6 +453,18 @@ type ViewingKeyDerivationFunctionType = Hasher; /// Viewing Key Derivation Function +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "ViewingKeyDerivationFunctionType: Deserialize<'de>", + serialize = "ViewingKeyDerivationFunctionType: Serialize", + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "ViewingKeyDerivationFunctionType: Clone"), @@ -523,6 +562,11 @@ impl protocol::ViewingKeyDerivationFunction for ViewingKeyDerivationFu } /// Incoming Encryption Scheme Converter +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct IncomingEncryptionSchemeConverter(PhantomData); @@ -723,6 +767,11 @@ pub const AES_CIPHERTEXT_SIZE: usize = AES_PLAINTEXT_SIZE + 16; pub type AES = aes::FixedNonceAesGcm; /// Incoming AES Encryption Scheme +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct IncomingAESEncryptionScheme(PhantomData); @@ -827,6 +876,11 @@ impl encryption::Decrypt for IncomingAESEncryptionScheme { } /// Incoming AES Converter +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct IncomingAESConverter(PhantomData); @@ -1001,6 +1055,11 @@ pub type IncomingBaseAES = encryption::convert::key::Converter< >; /// Utxo Accumulator Item Hash Domain Tag +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct UtxoAccumulatorItemHashDomainTag; @@ -1026,6 +1085,18 @@ type UtxoAccumulatorItemHashType = Hasher; /// Utxo Accumulator Item Hash +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "UtxoAccumulatorItemHashType: Deserialize<'de>", + serialize = "UtxoAccumulatorItemHashType: Serialize", + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "UtxoAccumulatorItemHashType: Clone"), @@ -1122,6 +1193,11 @@ pub type LeafHash = merkle_tree::IdentityLeafHash; pub type LeafHashVar = merkle_tree::IdentityLeafHash; /// Inner Hash Domain Tag +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct InnerHashDomainTag; @@ -1291,6 +1367,11 @@ impl merkle_tree::test::HashParameterSampling for MerkleTreeConfiguration { } /// Nullifier Commitment Scheme Domain Tag +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct NullifierCommitmentSchemeDomainTag; @@ -1316,6 +1397,18 @@ type NullifierCommitmentSchemeType = Hasher; /// Nullifier Commitment Scheme +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "NullifierCommitmentSchemeType: Deserialize<'de>", + serialize = "NullifierCommitmentSchemeType: Serialize", + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "NullifierCommitmentSchemeType: Clone"), @@ -1427,6 +1520,11 @@ pub const OUT_AES_CIPHERTEXT_SIZE: usize = OUT_AES_PLAINTEXT_SIZE + 16; pub type OutAes = aes::FixedNonceAesGcm; /// Outgoing AES Encryption Scheme +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct OutgoingAESEncryptionScheme(PhantomData); @@ -1531,6 +1629,11 @@ impl encryption::Decrypt for OutgoingAESEncryptionScheme { } /// Outgoing AES Converter +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct OutgoingAESConverter(PhantomData); @@ -1700,6 +1803,11 @@ pub type OutgoingBaseAES = encryption::convert::key::Converter< >; /// Address Partition Function +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct AddressPartitionFunction; @@ -1762,6 +1870,11 @@ impl Sample for AddressPartitionFunction { } /// Schnorr Hash Function +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct SchnorrHashFunction; diff --git a/manta-pay/src/config/utxo_utilities.rs b/manta-pay/src/config/utxo_utilities.rs deleted file mode 100644 index 9067946fc..000000000 --- a/manta-pay/src/config/utxo_utilities.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2019-2022 Manta Network. -// This file is part of manta-rs. -// -// manta-rs is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// manta-rs 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with manta-rs. If not, see . - -//! UTXO Utilities - -use alloc::vec::Vec; -use manta_crypto::{ - arkworks::{ - constraint::SynthesisError, - ff::Field, - r1cs_std::{R1CSVar, ToBytesGadget}, - }, - eclair::num::UnsignedInteger, -}; - -/// From a little endian vector `v` of a certain length, it returns a vector of length `n` after removing some zeroes. -/// -/// # Panics -/// -/// Panics if `vec` length is not at least equal to `n` or if any of it's elements -/// beyond index `n` are non-zero. -pub fn from_little_endian(vec: Vec, n: usize) -> Vec -where - T: manta_crypto::eclair::num::Zero + PartialEq + Clone, -{ - let vec_len = vec.len(); - assert!(vec_len >= n, "Vector length must be at least equal to N"); - assert!( - vec[n..vec_len].iter().all(|z| *z == T::zero(&mut ())), - "Extra elements of `vec` must be zero" - ); - vec[0..n].to_vec() -} - -/// Extracts a vector of bytes from `u`, where `u` implements -/// `ToBytesGadget` -pub fn bytes_from_gadget(u: U) -> Result, SynthesisError> -where - U: ToBytesGadget, - F: Field, -{ - u.to_bytes()?.into_iter().map(|x| x.value()).collect() -} - -/// Extracts a vector of bytes from an [`UnsignedInteger`]. -pub fn bytes_from_unsigned( - u: &UnsignedInteger, -) -> Result, SynthesisError> -where - T: ToBytesGadget, - F: Field, -{ - u.to_bytes()?.into_iter().map(|x| x.value()).collect() -} diff --git a/manta-pay/src/crypto/poseidon/encryption.rs b/manta-pay/src/crypto/poseidon/encryption.rs index f2bbc029e..74af7e5e6 100644 --- a/manta-pay/src/crypto/poseidon/encryption.rs +++ b/manta-pay/src/crypto/poseidon/encryption.rs @@ -558,7 +558,6 @@ where } /// Fixed Encryption Configuration -/* TODO: #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), @@ -571,7 +570,6 @@ where deny_unknown_fields ) )] -*/ #[derive(derivative::Derivative)] #[derivative( Clone(bound = "S::Field: Clone"), diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index 2e8d95b44..06d332cc2 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -25,7 +25,10 @@ //! [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki use alloc::{format, string::String, vec::Vec}; -use core::marker::PhantomData; +use core::{ + hash::{Hash, Hasher}, + marker::PhantomData, +}; use manta_accounting::key::{self, AccountIndex}; use manta_crypto::rand::{CryptoRng, RngCore}; use manta_util::{create_seal, seal, Array}; @@ -133,7 +136,7 @@ type SeedBytes = Array; serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] +#[derivative(Clone(bound = ""), Debug, Eq, Hash, PartialEq)] pub struct KeySecret where C: CoinType, @@ -227,7 +230,7 @@ where derive(Deserialize, Serialize), serde(crate = "manta_util::serde", deny_unknown_fields, try_from = "String") )] -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Mnemonic( /// Underlying BIP39 Mnemonic #[cfg_attr(feature = "serde", serde(serialize_with = "Mnemonic::serialize"))] @@ -288,6 +291,13 @@ impl PartialEq for Mnemonic { } } +impl Hash for Mnemonic { + #[inline] + fn hash(&self, state: &mut H) { + self.as_ref().hash(state) + } +} + impl TryFrom for Mnemonic { type Error = Error; diff --git a/manta-pay/src/signer/base.rs b/manta-pay/src/signer/base.rs index d0c7649e4..05eead64d 100644 --- a/manta-pay/src/signer/base.rs +++ b/manta-pay/src/signer/base.rs @@ -25,10 +25,10 @@ use crate::{ key::{CoinType, KeySecret, Testnet}, signer::{AssetMetadata, Checkpoint}, }; -use alloc::collections::BTreeMap; +use alloc::{collections::BTreeMap, vec, vec::Vec}; use core::{cmp, mem}; use manta_accounting::{ - asset::HashAssetMap, + asset::BTreeAssetMap, key::{AccountCollection, AccountIndex, DeriveAddresses}, transfer::{utxo::protocol, Identifier, IdentityVerificationError, SpendingKey}, wallet::{ @@ -88,7 +88,7 @@ impl wallet::signer::Configuration for Config { type Account = KeySecret; type Checkpoint = Checkpoint; type UtxoAccumulator = UtxoAccumulator; - type AssetMap = HashAssetMap, Self::AssetId, Self::AssetValue>; + type AssetMap = BTreeAssetMap, Self::AssetId, Self::AssetValue>; type AssetMetadata = AssetMetadata; type Rng = ChaCha20Rng; } @@ -198,6 +198,12 @@ pub type SignerState = wallet::signer::SignerState; /// Signer Base Type pub type Signer = wallet::signer::Signer; +/// Wallet Associated to [`Signer`] +pub type Wallet = wallet::Wallet; + +/// Wallet Error Type +pub type WalletError = wallet::Error; + /// Runs the identity verification method on `identity_proof` with [`Config`] and /// [`UtxoAccumulator`], checking that `virtual_asset` is opaque and not zero for extra security. #[inline] diff --git a/manta-pay/src/signer/client/http.rs b/manta-pay/src/signer/client/http.rs index 05027fe59..f49bf1ebe 100644 --- a/manta-pay/src/signer/client/http.rs +++ b/manta-pay/src/signer/client/http.rs @@ -21,8 +21,8 @@ use crate::{ signer::{ client::network::{Message, Network}, AssetMetadata, Checkpoint, GetRequest, IdentityRequest, IdentityResponse, SignError, - SignRequest, SignResponse, SyncError, SyncRequest, SyncResponse, TransactionDataRequest, - TransactionDataResponse, + SignRequest, SignResponse, SignWithTransactionDataResult, SyncError, SyncRequest, + SyncResponse, TransactionDataRequest, TransactionDataResponse, }, }; use alloc::boxed::Box; @@ -131,4 +131,16 @@ impl signer::Connection for Client { .await }) } + + #[inline] + fn sign_with_transaction_data( + &mut self, + request: SignRequest, + ) -> LocalBoxFutureResult { + Box::pin(async move { + self.base + .post("sign_with_transaction_data", &self.wrap_request(request)) + .await + }) + } } diff --git a/manta-pay/src/signer/client/mod.rs b/manta-pay/src/signer/client/mod.rs index 4cbcda8f2..c53742837 100644 --- a/manta-pay/src/signer/client/mod.rs +++ b/manta-pay/src/signer/client/mod.rs @@ -16,6 +16,8 @@ //! Signer Client Implementations +pub mod network; + #[cfg(feature = "http")] #[cfg_attr(doc_cfg, doc(cfg(feature = "http")))] pub mod http; @@ -23,7 +25,3 @@ pub mod http; #[cfg(feature = "websocket")] #[cfg_attr(doc_cfg, doc(cfg(feature = "websocket")))] pub mod websocket; - -#[cfg(feature = "network")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "network")))] -pub mod network; diff --git a/manta-pay/src/signer/client/network.rs b/manta-pay/src/signer/client/network.rs index 117c1aa2c..17f1773ae 100644 --- a/manta-pay/src/signer/client/network.rs +++ b/manta-pay/src/signer/client/network.rs @@ -53,6 +53,29 @@ impl Display for Network { } } +impl From for usize { + #[inline] + fn from(value: Network) -> Self { + match value { + Network::Dolphin => 0, + Network::Calamari => 1, + Network::Manta => 2, + } + } +} + +/// Network Error +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum NetworkError { + /// Nonexistent Wallet + NonexistentWallet(Network), +} + /// Network-Specific Data #[cfg_attr( feature = "serde", diff --git a/manta-pay/src/signer/client/websocket.rs b/manta-pay/src/signer/client/websocket.rs index c4e7ac3ac..dae40d28a 100644 --- a/manta-pay/src/signer/client/websocket.rs +++ b/manta-pay/src/signer/client/websocket.rs @@ -22,8 +22,8 @@ use crate::{ config::{utxo::Address, Config}, signer::{ AssetMetadata, Checkpoint, GetRequest, IdentityRequest, IdentityResponse, SignError, - SignRequest, SignResponse, SyncError, SyncRequest, SyncResponse, TransactionDataRequest, - TransactionDataResponse, + SignRequest, SignResponse, SignWithTransactionDataResult, SyncError, SyncRequest, + SyncResponse, TransactionDataRequest, TransactionDataResponse, }, }; use alloc::boxed::Box; @@ -168,4 +168,12 @@ impl signer::Connection for Client { ) -> LocalBoxFutureResult { Box::pin(async move { self.send("identity", request).await }) } + + #[inline] + fn sign_with_transaction_data( + &mut self, + request: SignRequest, + ) -> LocalBoxFutureResult { + Box::pin(async move { self.send("sign_with_transaction_data", request).await }) + } } diff --git a/manta-pay/src/signer/functions.rs b/manta-pay/src/signer/functions.rs new file mode 100644 index 000000000..06e5e25c5 --- /dev/null +++ b/manta-pay/src/signer/functions.rs @@ -0,0 +1,170 @@ +// Copyright 2019-2022 Manta Network. +// This file is part of manta-rs. +// +// manta-rs is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// manta-rs 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with manta-rs. If not, see . + +//! Manta Pay Signer Functions + +use crate::{ + config::{ + Address, Config, IdentifiedAsset, IdentityProof, MultiProvingContext, Parameters, + Transaction, TransactionData, TransferPost, UtxoAccumulatorModel, + }, + key::{KeySecret, Mnemonic}, + signer::{ + base::{Signer, SignerParameters, UtxoAccumulator}, + AccountTable, AssetMap, Checkpoint, SignResult, SignerRng, SyncRequest, SyncResult, + }, +}; +use manta_accounting::wallet::signer::{functions, StorageState}; +use manta_crypto::{accumulator::Accumulator, rand::FromEntropy}; + +/// Builds a new [`Signer`] from `mnemonic`, `password`, `parameters`, `proving_context` +/// and `utxo_accumulator`. +#[inline] +pub fn new_signer( + mnemonic: Mnemonic, + password: &str, + parameters: Parameters, + proving_context: MultiProvingContext, + utxo_accumulator: UtxoAccumulator, +) -> Signer { + Signer::new( + AccountTable::new(KeySecret::new(mnemonic, password)), + parameters, + proving_context, + utxo_accumulator, + FromEntropy::from_entropy(), + ) +} + +/// Builds a new [`Signer`] from `mnemonic`, `password`, `parameters`, `proving_context` +/// and `utxo_accumulator_model`. +/// +/// # Implementation Note +/// +/// The signer initialized in this way has an empty state and must be synchronized from scratch, +/// which is a time-consuming operation. One should favor the `new_signer` and +/// `initialize_signer_from_storage` functions when possible. +#[inline] +pub fn new_signer_from_model( + mnemonic: Mnemonic, + password: &str, + parameters: Parameters, + proving_context: MultiProvingContext, + utxo_accumulator_model: &UtxoAccumulatorModel, +) -> Signer { + new_signer( + mnemonic, + password, + parameters, + proving_context, + Accumulator::empty(utxo_accumulator_model), + ) +} + +/// Initializes a [`Signer`] from `storage_state`, `mnemonic`, `password`, +/// `parameters` and `proving_context`. +pub fn initialize_signer_from_storage( + storage_state: &StorageState, + mnemonic: Mnemonic, + password: &str, + parameters: Parameters, + proving_context: MultiProvingContext, +) -> Signer { + storage_state.initialize_signer( + AccountTable::new(KeySecret::new(mnemonic, password)), + parameters, + proving_context, + ) +} + +/// Updates `assets`, `checkpoint` and `utxo_accumulator`, returning the new asset distribution. +#[allow(clippy::result_large_err)] +#[inline] +pub fn sync( + parameters: &SignerParameters, + accounts: &AccountTable, + assets: &mut AssetMap, + checkpoint: &mut Checkpoint, + utxo_accumulator: &mut UtxoAccumulator, + request: SyncRequest, + rng: &mut SignerRng, +) -> SyncResult { + functions::sync( + parameters, + accounts, + assets, + checkpoint, + utxo_accumulator, + request, + rng, + ) +} + +/// Signs the `transaction`, generating transfer posts. +#[inline] +pub fn sign( + parameters: &SignerParameters, + accounts: &AccountTable, + assets: &AssetMap, + utxo_accumulator: &mut UtxoAccumulator, + transaction: Transaction, + rng: &mut SignerRng, +) -> SignResult { + functions::sign( + parameters, + accounts, + assets, + utxo_accumulator, + transaction, + rng, + ) +} + +/// Returns the [`Address`] corresponding to `accounts`. +#[inline] +pub fn address(parameters: &SignerParameters, accounts: &AccountTable) -> Address { + functions::address(parameters, accounts) +} + +/// Returns the associated [`TransactionData`] of `post`. Returns `None` if `post` has an invalid shape, +/// or if `accounts` doesn't own the underlying assets in `post`. +#[inline] +pub fn transaction_data( + parameters: &SignerParameters, + accounts: &AccountTable, + post: TransferPost, +) -> Option { + functions::transaction_data(parameters, accounts, post) +} + +/// Generates an [`IdentityProof`] for `identified_asset` by signing a +/// virtual [`ToPublic`](manta_accounting::transfer::canonical::ToPublic) transaction. +#[inline] +pub fn identity_proof( + parameters: &SignerParameters, + accounts: &AccountTable, + utxo_accumulator_model: &UtxoAccumulatorModel, + identified_asset: IdentifiedAsset, + rng: &mut SignerRng, +) -> Option { + functions::identity_proof( + parameters, + accounts, + utxo_accumulator_model, + identified_asset, + rng, + ) +} diff --git a/manta-pay/src/signer/mod.rs b/manta-pay/src/signer/mod.rs index 3ea4c18d7..7218ee92f 100644 --- a/manta-pay/src/signer/mod.rs +++ b/manta-pay/src/signer/mod.rs @@ -16,6 +16,7 @@ //! Manta Pay Signer Tools +use crate::config::{utxo::Checkpoint, Config}; use alloc::{ format, string::{String, ToString}, @@ -23,9 +24,6 @@ use alloc::{ use core::ops::{Div, Sub}; use manta_accounting::wallet::signer; -#[cfg(feature = "groth16")] -use crate::config::{utxo::Checkpoint, Config}; - #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; @@ -35,6 +33,10 @@ pub mod client; #[cfg_attr(doc_cfg, doc(cfg(feature = "wallet")))] pub mod base; +#[cfg(feature = "wallet")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "wallet")))] +pub mod functions; + /// Synchronization Request pub type SyncRequest = signer::SyncRequest; @@ -71,6 +73,21 @@ pub type IdentityRequest = signer::IdentityRequest; /// Identity Verification Response pub type IdentityResponse = signer::IdentityResponse; +/// Account Table Type +pub type AccountTable = signer::AccountTable; + +/// AssetMap Type +pub type AssetMap = ::AssetMap; + +/// Rng Type +pub type SignerRng = ::Rng; + +/// Sign With Transaction Data Result +pub type SignWithTransactionDataResult = signer::SignWithTransactionDataResult; + +/// Sign With Transaction Data Response +pub type SignWithTransactionDataResponse = signer::SignWithTransactionDataResponse; + /// Receiving Key Request #[cfg_attr( feature = "serde", diff --git a/manta-pay/src/test/signer.rs b/manta-pay/src/test/signer.rs index 6df94dcbb..d61ad344c 100644 --- a/manta-pay/src/test/signer.rs +++ b/manta-pay/src/test/signer.rs @@ -22,11 +22,12 @@ use crate::{ signer::base::identity_verification, simulation::sample_signer, }; -use manta_accounting::transfer::{IdentifiedAsset, Identifier}; +use manta_accounting::transfer::{canonical::Transaction, IdentifiedAsset, Identifier}; use manta_crypto::{ arkworks::constraint::fp::Fp, rand::{fuzz::Fuzz, OsRng, Rand}, }; +use manta_util::vec::VecExt; /// Checks the generation and verification of [`IdentityProof`](manta_accounting::transfer::IdentityProof)s. #[test] @@ -108,3 +109,32 @@ fn identity_proof_test() { "Verification should have failed" ); } + +/// Signs a [`ToPrivate`](manta_accounting::transfer::canonical::ToPrivate) transaction, computes its +/// [`TransactionData`](manta_accounting::transfer::canonical::TransactionData), and checks its correctness. +#[test] +fn transaction_data_test() { + let mut rng = OsRng; + let directory = tempfile::tempdir().expect("Unable to generate temporary test directory."); + let (proving_context, _, parameters, utxo_accumulator_model) = + load_parameters(directory.path()).expect("Failed to load parameters"); + let mut signer = sample_signer( + &proving_context, + ¶meters, + &utxo_accumulator_model, + &mut rng, + ); + let transaction = Transaction::ToPrivate(rng.gen()); + let response = signer + .sign_with_transaction_data(transaction) + .expect("Signing a ToPrivate transaction is not allowed to fail.") + .0 + .take_first(); + let utxo = response.0.body.receiver_posts.take_first().utxo; + assert!( + response + .1 + .check_transaction_data(¶meters, &signer.address(), &vec![utxo]), + "Invalid Transaction Data" + ); +} diff --git a/manta-trusted-setup/src/bin/groth16_phase2_prepare.rs b/manta-trusted-setup/src/bin/groth16_phase2_prepare.rs index 0887a6bf0..a516b5e9f 100644 --- a/manta-trusted-setup/src/bin/groth16_phase2_prepare.rs +++ b/manta-trusted-setup/src/bin/groth16_phase2_prepare.rs @@ -22,7 +22,7 @@ use clap::Parser; use manta_trusted_setup::groth16::ceremony::{ - config::ppot::{Config, Record, Registry}, + config::ppot::{Config, Registry}, coordinator::prepare, CeremonyError, }; @@ -42,7 +42,7 @@ impl Arguments { /// Prepares for phase 2 ceremony #[inline] pub fn run(self) -> Result<(), CeremonyError> { - prepare::(self.phase_one_param_path, self.recovery_directory); + prepare::(self.phase_one_param_path, self.recovery_directory); Ok(()) } } diff --git a/manta-trusted-setup/src/groth16/ceremony/coordinator.rs b/manta-trusted-setup/src/groth16/ceremony/coordinator.rs index 551fc3c8b..5f6525c4e 100644 --- a/manta-trusted-setup/src/groth16/ceremony/coordinator.rs +++ b/manta-trusted-setup/src/groth16/ceremony/coordinator.rs @@ -19,7 +19,7 @@ use crate::{ ceremony::{ participant::{Participant, Priority}, - registry::{self, csv::Record, Registry}, + registry::{self, Registry}, signature::{Nonce, SignedMessage}, util::{deserialize_from_file, serialize_into_file}, }, @@ -443,7 +443,7 @@ where /// Prepare by initalizing each circuit's prover key, challenge hash and saving /// to file. Creates a `_registry_0` file containing an empty registry. /// TODO: Generalize ProvingKeyHasher Output type and curves. -pub fn prepare(phase_one_param_path: PathBuf, target_path: PathBuf) +pub fn prepare(phase_one_param_path: PathBuf, target_path: PathBuf) where C: Ceremony + Configuration @@ -461,8 +461,6 @@ where Registry = R, >, R: Debug, - T: Record, - T::Error: Debug, C::Identifier: Debug + Copy, { use memmap::MmapOptions; diff --git a/manta-trusted-setup/src/groth16/mpc.rs b/manta-trusted-setup/src/groth16/mpc.rs index 4cfca671f..abe445825 100644 --- a/manta-trusted-setup/src/groth16/mpc.rs +++ b/manta-trusted-setup/src/groth16/mpc.rs @@ -614,7 +614,7 @@ pub mod util { message: "Unable to deserialize state at provided path".to_string(), })?, }; - let proving_context = ProvingContext(state.0); + let proving_context = ProvingContext::new(state.0); proving_context .encode(IoWriter(pk_file)) .map_err(|_| UnexpectedError::Serialization {