Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DO NOT MERGE: SBT signer sync #335

Closed
wants to merge 64 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
ae47774
checkpoint
SupremoUGH Mar 14, 2023
df6edd4
checkpoint
SupremoUGH Mar 15, 2023
164057a
checkpoint
SupremoUGH Mar 15, 2023
629bdec
Merge branch 'main' into signer_initial_sync_checkout
SupremoUGH Mar 15, 2023
60c5eb8
merkle tree functions
SupremoUGH Mar 15, 2023
6496e48
accumulator trait
SupremoUGH Mar 15, 2023
e6d73f9
signer functions
SupremoUGH Mar 15, 2023
8444d01
signer initial sync
SupremoUGH Mar 15, 2023
1c18f58
wallet has method as well
SupremoUGH Mar 15, 2023
091eb31
tests
SupremoUGH Mar 16, 2023
0c78f26
change dep
SupremoUGH Mar 16, 2023
be369ad
fixed dep
SupremoUGH Mar 16, 2023
8053258
dep
SupremoUGH Mar 16, 2023
ca0ad1d
rearranged tests
SupremoUGH Mar 17, 2023
9cc2e1f
restored cargo.toml
SupremoUGH Mar 17, 2023
68ce7ac
clippy
SupremoUGH Mar 17, 2023
d9c2a77
changed nullifier data and batched the initial sync
SupremoUGH Mar 17, 2023
e57d2b6
final design
SupremoUGH Mar 20, 2023
1d83f3c
export type CurrentPath
SupremoUGH Mar 20, 2023
976ae74
small error
SupremoUGH Mar 20, 2023
e16c651
debugging
SupremoUGH Mar 21, 2023
0c1506d
debug
SupremoUGH Mar 21, 2023
bcd0aff
debugging
SupremoUGH Mar 21, 2023
69a088b
changed default impl
SupremoUGH Mar 21, 2023
087e7b6
first round of corrections
SupremoUGH Mar 22, 2023
09b5969
debug removed
SupremoUGH Mar 22, 2023
aa4b43b
unused imports
SupremoUGH Mar 22, 2023
30fe0d8
simplified initial sync. added reset to initial sync
SupremoUGH Mar 22, 2023
c2a4610
simulation restored
SupremoUGH Mar 22, 2023
c3bd754
testing fixed
SupremoUGH Mar 22, 2023
c99cf89
test fixed
SupremoUGH Mar 22, 2023
8bd829c
fixed feature issue
SupremoUGH Mar 22, 2023
c87f67b
changelog
SupremoUGH Mar 22, 2023
91901de
signer method in wallet
SupremoUGH Mar 22, 2023
fe4ed12
Merge branch 'main' into signer_initial_sync_checkout
SupremoUGH Mar 23, 2023
090b67e
comments addressed
SupremoUGH Mar 24, 2023
e76bb56
inner tree logic
SupremoUGH Mar 24, 2023
b5d5bab
all trees take batch insertions
SupremoUGH Mar 27, 2023
840b0cc
impl for forest
SupremoUGH Mar 28, 2023
ba4a6d8
bug fixed
SupremoUGH Mar 28, 2023
1a358c7
test
SupremoUGH Mar 28, 2023
a0eaff6
test for forest
SupremoUGH Mar 28, 2023
5278015
remove debug
SupremoUGH Mar 28, 2023
6eaa823
signer updated
SupremoUGH Mar 28, 2023
2a9212b
small check
SupremoUGH Mar 28, 2023
909c3ba
merge
SupremoUGH Mar 28, 2023
725d813
batch_insert doesnt panic
SupremoUGH Mar 29, 2023
5259da7
docs
SupremoUGH Mar 29, 2023
476fcba
docs
SupremoUGH Mar 29, 2023
b11308c
batch insertion till full capacity
SupremoUGH Mar 29, 2023
d5bbb4a
changelog
SupremoUGH Mar 29, 2023
eecf9ff
truncate
SupremoUGH Mar 29, 2023
d4c8718
expose auth context
SupremoUGH Mar 29, 2023
d7977e1
comments addressed
SupremoUGH Apr 3, 2023
b12df28
try_load
SupremoUGH Apr 3, 2023
eb3b2f3
clippy
SupremoUGH Apr 3, 2023
c17e3b9
Merge branch 'main' into batch_insert
bhgomes Apr 4, 2023
7ff584a
merge main
SupremoUGH Apr 4, 2023
e43516e
comment addressed
SupremoUGH Apr 4, 2023
b167040
sbt signer sync
SupremoUGH Apr 4, 2023
c4f8704
sbt sync wallet
SupremoUGH Apr 5, 2023
f2fd6fe
docs
SupremoUGH Apr 5, 2023
b803418
changelog
SupremoUGH Apr 5, 2023
91f85c9
merge main
SupremoUGH Apr 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]
### Added
- [\#335](https://github.com/Manta-Network/manta-rs/pull/335) SBT synchronization method for the signer.
- [\#330](https://github.com/Manta-Network/manta-rs/pull/330) Merkle tree batch insertions.

### Changed
Expand Down
113 changes: 100 additions & 13 deletions manta-accounting/src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,36 @@ where
Ok(())
}

/// Pulls data from the ledger, synchronizing the wallet and balance state. This method loops
/// continuously calling [`sbt_sync_partial`](Self::sbt_sync_partial) until all the ledger data has
/// arrived at and has been synchronized with the wallet.
///
/// # Failure Conditions
///
/// This method returns an element of type [`Error`] on failure, which can result from any
/// number of synchronization issues between the wallet, the ledger, and the signer. See the
/// [`InconsistencyError`] type for more information on the kinds of errors that can occur and
/// how to resolve them.
///
/// # Note
///
/// In general, this method does not update the [`Utxo`] accumulator, thus making the new assets
/// effectively non-spendable. Therefore, this method should only be used when the pallet does not
/// allow [`PrivateTransfer`]s or [`ToPublic`] transactions, for example in the case of
/// Soul-Bound Tokens (SBTs).
///
/// [`Utxo`]: Configuration::Utxo
/// [`PrivateTransfer`]: crate::transfer::canonical::PrivateTransfer
/// [`ToPublic`]: crate::transfer::canonical::ToPublic
#[inline]
pub async fn sbt_sync(&mut self) -> Result<(), Error<C, L, S>>
where
L: ledger::Read<SyncData<C>, Checkpoint = S::Checkpoint>,
{
while self.sbt_sync_partial().await?.is_continue() {}
Ok(())
}

/// Pulls data from the ledger, synchronizing the wallet and balance state. This method
/// builds a [`InitialSyncRequest`] by continuously calling [`read`](ledger::Read::read)
/// until all the ledger data has arrived. Once the request is built, it executes
Expand All @@ -298,7 +328,7 @@ where
S::Checkpoint: signer::Checkpoint<C>,
{
let mut is_continue = true;
let mut checkpoint = Default::default();
let mut checkpoint = S::Checkpoint::default();
let mut request = InitialSyncRequest::<C>::default();
let parameters = self
.signer
Expand All @@ -309,15 +339,12 @@ where
let ReadResponse {
should_continue,
data,
} = self
.ledger
.read(&checkpoint)
.await
.map_err(Error::LedgerConnectionError)?;
} = self.read_from_ledger().await?;
let more = InitialSyncRequest::from_initial_sync_data(&parameters, data);
is_continue = should_continue;
request.extend_with_data(&parameters, data);
checkpoint
.update_from_utxo_count(request.utxo_data.iter().map(|utxos| utxos.len()).collect())
.update_from_utxo_count(more.utxo_data.iter().map(|utxos| utxos.len()).collect());
request.extend(more);
}
self.signer_initial_sync(request).await?;
Ok(())
Expand All @@ -341,6 +368,56 @@ where
self.sync_with().await
}

/// Reads data from the ledger.
#[inline]
async fn read_from_ledger<D>(&mut self) -> Result<ReadResponse<D>, Error<C, L, S>>
where
L: ledger::Read<D, Checkpoint = S::Checkpoint>,
{
self.ledger
.read(&self.checkpoint)
.await
.map_err(Error::LedgerConnectionError)
}

/// Pulls data from the ledger, synchronizing the wallet and balance state. This method returns
/// a [`ControlFlow`] for matching against to determine if the wallet requires more
/// synchronization.
///
/// # Failure Conditions
///
/// This method returns an element of type [`Error`] on failure, which can result from any
/// number of synchronization issues between the wallet, the ledger, and the signer. See the
/// [`InconsistencyError`] type for more information on the kinds of errors that can occur and
/// how to resolve them.
///
/// # Note
///
/// In general, this method does not update the [`Utxo`] accumulator, thus making the new assets
/// effectively non-spendable. Therefore, this method should only be used when the pallet does not
/// allow [`PrivateTransfer`]s or [`ToPublic`] transactions, for example in the case of
/// Soul-Bound Tokens (SBTs).
///
/// [`Utxo`]: Configuration::Utxo
/// [`PrivateTransfer`]: crate::transfer::canonical::PrivateTransfer
/// [`ToPublic`]: crate::transfer::canonical::ToPublic
#[inline]
pub async fn sbt_sync_partial(&mut self) -> Result<ControlFlow, Error<C, L, S>>
where
L: ledger::Read<SyncData<C>, Checkpoint = S::Checkpoint>,
{
let ReadResponse {
should_continue,
data,
} = self.read_from_ledger().await?;
self.signer_sbt_sync(SyncRequest {
origin_checkpoint: self.checkpoint.clone(),
data,
})
.await?;
Ok(ControlFlow::should_continue(should_continue))
}

/// Pulls data from the ledger, synchronizing the wallet and balance state.
#[inline]
async fn sync_with(&mut self) -> Result<ControlFlow, Error<C, L, S>>
Expand All @@ -350,11 +427,7 @@ where
let ReadResponse {
should_continue,
data,
} = self
.ledger
.read(&self.checkpoint)
.await
.map_err(Error::LedgerConnectionError)?;
} = self.read_from_ledger().await?;
self.signer_sync(SyncRequest {
origin_checkpoint: self.checkpoint.clone(),
data,
Expand Down Expand Up @@ -431,6 +504,20 @@ where
self.process_sync_response(response)
}

/// Performs an sbt synchronization with the signer against the given `request`.
#[inline]
async fn signer_sbt_sync(
&mut self,
request: SyncRequest<C, S::Checkpoint>,
) -> Result<(), Error<C, L, S>> {
let response = self
.signer
.sbt_sync(request)
.await
.map_err(Error::SignerConnectionError)?;
self.process_sync_response(response)
}

/// Checks if `transaction` can be executed on the balance state of `self`, returning the
/// kind of update that should be performed on the balance state if the transaction is
/// successfully posted to the ledger.
Expand Down
170 changes: 131 additions & 39 deletions manta-accounting/src/wallet/signer/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ fn insert_next_item<C>(
utxo_accumulator: &mut C::UtxoAccumulator,
assets: &mut C::AssetMap,
parameters: &Parameters<C>,
utxo: Utxo<C>,
identified_asset: IdentifiedAsset<C>,
nullifiers: &mut Vec<Nullifier<C>>,
deposit: &mut Vec<Asset<C>>,
Expand All @@ -138,26 +137,24 @@ fn insert_next_item<C>(
C: Configuration,
{
let IdentifiedAsset::<C> { identifier, asset } = identified_asset;
let (_, computed_utxo, nullifier) = parameters.derive_spend(
let (_, 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::<C>(parameters, &utxo));
if !asset.is_zero() {
deposit.push(asset.clone());
}
assets.insert(identifier, asset);
return;
if let Some(index) = nullifiers
.iter()
.position(move |n| n.is_related(&nullifier))
{
nullifiers.remove(index);
} else {
utxo_accumulator.insert(&item_hash::<C>(parameters, &utxo));
if !asset.is_zero() {
deposit.push(asset.clone());
}
assets.insert(identifier, asset);
return;
}
utxo_accumulator.insert_nonprovable(&item_hash::<C>(parameters, &utxo));
}
Expand Down Expand Up @@ -231,7 +228,6 @@ where
utxo_accumulator,
assets,
parameters,
utxo,
transfer::utxo::IdentifiedAsset::new(identifier, asset),
&mut nullifiers,
&mut deposit,
Expand Down Expand Up @@ -275,6 +271,53 @@ where
}
}

/// Updates the internal ledger state, returning the new asset distribution.
#[allow(clippy::too_many_arguments)]
#[inline]
fn sbt_sync_with<C, I>(
authorization_context: &mut AuthorizationContext<C>,
assets: &mut C::AssetMap,
checkpoint: &mut C::Checkpoint,
parameters: &Parameters<C>,
inserts: I,
utxo_count: Vec<usize>,
nullifier_count: usize,
is_partial: bool,
) -> SyncResponse<C, C::Checkpoint>
where
C: Configuration,
I: Iterator<Item = (Utxo<C>, Note<C>)>,
{
let mut deposit = Vec::new();
let decryption_key = parameters.derive_decryption_key(authorization_context);
for (utxo, note) in inserts {
if let Some((identifier, asset)) = parameters.open_with_check(&decryption_key, &utxo, note)
{
if !asset.is_zero() {
deposit.push(asset.clone());
}
assets.insert(identifier, asset);
}
}
checkpoint.update_from_nullifiers(nullifier_count);
checkpoint.update_from_utxo_count(utxo_count);
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: Default::default(),
}
} else {
BalanceUpdate::Full {
assets: assets.assets().into(),
}
},
}
}

/// Builds the [`PreSender`] associated to `identifier` and `asset`.
#[inline]
fn build_pre_sender<C>(
Expand Down Expand Up @@ -619,17 +662,14 @@ where
account.address(&parameters.parameters)
}

/// Updates `assets`, `checkpoint` and `utxo_accumulator`, returning the new asset distribution.
/// Checks that the origin checkpoint in `request` is less or equal than `checkpoint`.
/// If it is strictly less, it prunes the data in `request` accordingly.
#[inline]
pub fn sync<C>(
fn prune_sync_request<C>(
parameters: &SignerParameters<C>,
authorization_context: &mut AuthorizationContext<C>,
assets: &mut C::AssetMap,
checkpoint: &mut C::Checkpoint,
utxo_accumulator: &mut C::UtxoAccumulator,
checkpoint: &C::Checkpoint,
mut request: SyncRequest<C, C::Checkpoint>,
rng: &mut C::Rng,
) -> Result<SyncResponse<C, C::Checkpoint>, SyncError<C::Checkpoint>>
) -> Result<(bool, SyncData<C>), SyncError<C::Checkpoint>>
where
C: Configuration,
{
Expand All @@ -647,24 +687,76 @@ where
parameters.parameters.utxo_accumulator_item_hash(),
checkpoint,
);
let SyncData {
Ok((has_pruned, request.data))
}
}

/// Updates `assets` and `checkpoint`, returning the new asset distribution.
#[inline]
pub fn sbt_sync<C>(
parameters: &SignerParameters<C>,
authorization_context: &mut AuthorizationContext<C>,
assets: &mut C::AssetMap,
checkpoint: &mut C::Checkpoint,
request: SyncRequest<C, C::Checkpoint>,
) -> Result<SyncResponse<C, C::Checkpoint>, SyncError<C::Checkpoint>>
where
C: Configuration,
{
let utxo_count = request.utxo_count(&parameters.parameters);
let (
has_pruned,
SyncData {
utxo_note_data,
nullifier_data,
} = request.data;
let response = sync_with::<C, _>(
authorization_context,
assets,
checkpoint,
utxo_accumulator,
&parameters.parameters,
utxo_note_data.into_iter(),
},
) = prune_sync_request(parameters, checkpoint, request)?;
Ok(sbt_sync_with(
authorization_context,
assets,
checkpoint,
&parameters.parameters,
utxo_note_data.into_iter(),
utxo_count,
nullifier_data.len(),
!has_pruned,
))
}

/// Updates `assets`, `checkpoint` and `utxo_accumulator`, returning the new asset distribution.
#[inline]
pub fn sync<C>(
parameters: &SignerParameters<C>,
authorization_context: &mut AuthorizationContext<C>,
assets: &mut C::AssetMap,
checkpoint: &mut C::Checkpoint,
utxo_accumulator: &mut C::UtxoAccumulator,
request: SyncRequest<C, C::Checkpoint>,
rng: &mut C::Rng,
) -> Result<SyncResponse<C, C::Checkpoint>, SyncError<C::Checkpoint>>
where
C: Configuration,
{
let (
has_pruned,
SyncData {
utxo_note_data,
nullifier_data,
!has_pruned,
rng,
);
utxo_accumulator.commit();
Ok(response)
}
},
) = prune_sync_request(parameters, checkpoint, request)?;
let response = sync_with::<C, _>(
authorization_context,
assets,
checkpoint,
utxo_accumulator,
&parameters.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`.
Expand Down
Loading