Skip to content

Commit

Permalink
Merge pull request #402 from anoma/bat/rename-ctx-to-wallet
Browse files Browse the repository at this point in the history
Rename Shielded Context to Shielded Wallet
  • Loading branch information
batconjurer authored Nov 11, 2024
2 parents 5c86402 + 142e14f commit ab03edd
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 137 deletions.
9 changes: 5 additions & 4 deletions packages/docs/pages/integrating-with-namada/sdk/context.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ Set up wallet storage for our addresses and keys:
let wallet = FsWalletUtils::new("./sdk-wallet".into());
```

Set up shielded context storage for the current state of the shielded pool:
Set up shielded wallet storage; instances of shielded wallets are not used directly, but are wrapped in a
`shielded context` which adds capabilities to a shielded wallet for querying nodes and performing I/O.
```rust
let shielded_ctx = FsShieldedUtils::new("./masp".into());
let shielded_ctx = ShieldedContext::new(FsShieldedUtils::new("./masp".into()));
```

Create an IO object to capture the input/output streams. Since we don't need to
Expand Down Expand Up @@ -68,7 +69,7 @@ async fn main() {
let http_client = HttpClient::new(url).unwrap();

let wallet = FsWalletUtils::new("./sdk-wallet".into());
let shielded_ctx = FsShieldedUtils::new("./masp".into());
let shielded_ctx = ShieldedContext::new(FsShieldedUtils::new("./masp".into()));
let null_io = NullIo;

let sdk = NamadaImpl::new(http_client, wallet, shielded_ctx, null_io)
Expand All @@ -77,7 +78,7 @@ async fn main() {
.chain_id(ChainId::from_str("local-net.b8f955720ab").unwrap());
}
```
The paths provided when creating `wallet` and `shielded_ctx` are the directories where the shielded context data and `wallet.toml` will be
The paths provided when creating `wallet` and `shielded_ctx` are the directories where the shielded wallet data and `wallet.toml` will be
saved to/read from.

<Callout type="info">
Expand Down
176 changes: 93 additions & 83 deletions packages/docs/pages/integrating-with-namada/sdk/shielded-sync.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
# Shielded Sync

Before performing MASP operations such as querying shielded balances, you will need to sync the local shielded context with the chain (this
Before performing MASP operations such as querying shielded balances, you will need to sync the local shielded wallet with the chain (this
is equivalent to running the [`namadac shielded-sync`](../../users/shielded-accounts/shielded-sync.mdx) command when using the CLI).

Syncing the shielded context directly from the chain is performance-intensive, so it's recommended to sync using a running
Syncing the shielded wallet directly from the chain is performance-intensive, so it's recommended to sync using a running
instance of the [`namada-masp-indexer`](https://github.com/anoma/namada-masp-indexer).

Instances of shielded wallets are not used directly, but are wrapped in a `shielded context` which adds capabilities
to a shielded wallet for querying nodes and performing I/O.

### Example code

This example demonstrates how to perform a shielded-sync from a masp-indexer endpoint, and save the synced context to disk.
Expand All @@ -16,93 +19,100 @@ reqwest = "0.11.4"
kdam = "0.5.2"
```

Then, the following code will sync the shielded context and save it to the directory provided when [creating the context](./context.mdx).
Then, the following function will sync the shielded wallet from a given chain context and save it to the directory
provided when [creating the context](./context.mdx).

```rust
use std::time::Duration;
use namada_sdk::masp::ExtendedSpendingKey;

use namada_sdk::Namada;
use namada_sdk::control_flow::install_shutdown_signal;
use namada_sdk::error::Error;
use namada_sdk::masp::{
ExtendedSpendingKey, FsShieldedUtils, MaspLocalTaskEnv,
ShieldedContext, ShieldedSyncConfig
};
use namada_sdk::wallet::DatedSpendingKey;
use namada_sdk::masp::{MaspLocalTaskEnv, ShieldedSyncConfig};

// the spend key with which to check note ownership
let spend_key = ExtendedSpendingKey::from_str("zsknam1q0medj45qqqqpq9wh90qd9c7d9f7n5xxn89h6dl54k0jfmucwn4yk7nykxwcrjmk4ylkdnlnn3wkkd9f3ul3nyw8hv5wlsfgklzr5ghzk2spzzwm05csvl2s3rn0aq7f9w4z7guul682yrw4hsmren2k2lgdp003uuj00lsd8nlevc8n32sz6j350up209980c04qdqcu97vh5476xv423k6jp58qn7hqjf9nvsvk8p8g5yfmqguu039q34c2euzhcpnca7vpp4pelqu6y87k")
.expect("Invalid spending key");

let dated_key = DatedSpendingKey::try_from(spend_key).expect("Error reading spend key");

// create a thread pool for the shielded sync
let env = MaspLocalTaskEnv::new(500).expect("could not create masp env");

// progress bars for displaying sync progress
let fetched = kdam::tqdm!(
total = 0,
desc = "fetched ",
animation = "fillup",
position = 0,
force_refresh = true,
dynamic_ncols = true,
miniters = 0,
mininterval = 0.05
);

let scanned = kdam::tqdm!(
total = 0,
desc = "scanned ",
animation = "fillup",
position = 1,
force_refresh = true,
dynamic_ncols = true,
miniters = 0,
mininterval = 0.05
);

let applied = kdam::tqdm!(
total = 0,
desc = "applied ",
animation = "fillup",
position = 2,
force_refresh = true,
dynamic_ncols = true,
miniters = 0,
mininterval = 0.05
);

// create a masp client to sync from the masp-indexer
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_secs(60))
.build()
.map_err(|err| {
Error::Other(format!("Failed to build http client: {err}"))
}).expect("could not create client");

let endpoint = "http://localhost:5000/api/v1".to_string();
let url = endpoint.as_str().try_into().map_err(|err| {
Error::Other(format!(
"Failed to parse API endpoint {endpoint:?}: {err}"
))
}).expect("failed to parse url");

let shielded_client = IndexerMaspClient::new(
client,
url,
true,
100,
);

let config = ShieldedSyncConfig::builder()
.client(shielded_client)
.fetched_tracker(fetched)
.scanned_tracker(scanned)
.applied_tracker(applied)
.shutdown_signal(install_shutdown_signal(false))
.build();

// shielded sync and save the results
println!("Syncing shielded context");
sdk.shielded_mut().await.sync(env, config, None, &[dated_key], &[]).await.expect("Could not sync shielded context");
println!("Shielded context synced");

async fn shielded_sync(sdk: impl Namada) {
// the spend key with which to check note ownership
let spend_key = ExtendedSpendingKey::from_str("zsknam1q0medj45qqqqpq9wh90qd9c7d9f7n5xxn89h6dl54k0jfmucwn4yk7nykxwcrjmk4ylkdnlnn3wkkd9f3ul3nyw8hv5wlsfgklzr5ghzk2spzzwm05csvl2s3rn0aq7f9w4z7guul682yrw4hsmren2k2lgdp003uuj00lsd8nlevc8n32sz6j350up209980c04qdqcu97vh5476xv423k6jp58qn7hqjf9nvsvk8p8g5yfmqguu039q34c2euzhcpnca7vpp4pelqu6y87k")
.expect("Invalid spending key");

let dated_key = DatedSpendingKey::try_from(spend_key).expect("Error reading spend key");

// create a thread pool for the shielded sync
let env = MaspLocalTaskEnv::new(500).expect("could not create masp env");

// progress bars for displaying sync progress
let fetched = kdam::tqdm!(
total = 0,
desc = "fetched ",
animation = "fillup",
position = 0,
force_refresh = true,
dynamic_ncols = true,
miniters = 0,
mininterval = 0.05
);

let scanned = kdam::tqdm!(
total = 0,
desc = "scanned ",
animation = "fillup",
position = 1,
force_refresh = true,
dynamic_ncols = true,
miniters = 0,
mininterval = 0.05
);

let applied = kdam::tqdm!(
total = 0,
desc = "applied ",
animation = "fillup",
position = 2,
force_refresh = true,
dynamic_ncols = true,
miniters = 0,
mininterval = 0.05
);

// create a masp client to sync from the masp-indexer
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_secs(60))
.build()
.map_err(|err| {
Error::Other(format!("Failed to build http client: {err}"))
}).expect("could not create client");

let endpoint = "http://localhost:5000/api/v1".to_string();
let url = endpoint.as_str().try_into().map_err(|err| {
Error::Other(format!(
"Failed to parse API endpoint {endpoint:?}: {err}"
))
}).expect("failed to parse url");

let shielded_client = IndexerMaspClient::new(
client,
url,
true,
100,
);

let config = ShieldedSyncConfig::builder()
.client(shielded_client)
.fetched_tracker(fetched)
.scanned_tracker(scanned)
.applied_tracker(applied)
.shutdown_signal(install_shutdown_signal(false))
.build();

// shielded sync and save the results
println!("Syncing shielded wallet");
sdk.shielded_mut().await.sync(env, config, None, &[dated_key], &[]).await.expect("Could not sync shielded wallet");
println!("Shielded wallet synced");
}
```

In the above example, one would replace the URL in the `endpoint` with the proper URL for the desired MASP indexer.
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,57 @@
You can construct shielded transfers similarly to transparent transfers, except that rather than providing implicit addresses as the `source` and `target`,
you will need to provide a spending key and payment address, respectively.

Before building your transaction, you will want to make sure the [shielded context is synced](./shielded-sync.mdx).
Before building your transaction, you will want to make sure the [shielded wallet is synced](./shielded-sync.mdx).

### Constructing the Transfer

This example assumes you have a spending key with sufficient shielded balance, a recipient payment address, and the
public key of an implicit account with gas funds.

```rust
use namada_sdk::Namada;
use namada_sdk::args::TxShieldedTransferData;
use namada_sdk::masp::PaymentAddress;
use namada_sdk::signing::default_sign;

let spend_key = ExtendedSpendingKey::from_str("zsknam1q0medj45qqqqpq9wh90qd9c7d9f7n5xxn89h6dl54k0jfmucwn4yk7nykxwcrjmk4ylkdnlnn3wkkd9f3ul3nyw8hv5wlsfgklzr5ghzk2spzzwm05csvl2s3rn0aq7f9w4z7guul682yrw4hsmren2k2lgdp003uuj00lsd8nlevc8n32sz6j350up209980c04qdqcu97vh5476xv423k6jp58qn7hqjf9nvsvk8p8g5yfmqguu039q34c2euzhcpnca7vpp4pelqu6y87k")
.expect("Invalid spending key");
let payment_addr = PaymentAddress::from_str("znam1vsz9wsge4u9c0thh38vhn2z9awzfqn4540ue4haxmcxl43srptrgrtlhn6r9cd9razawuakcclc")
.expect("Invalid payment address");

sdk.shielded_mut().await.load().await.expect("Could not load shielded context");

// specify the transfer arguments
let data = TxShieldedTransferData {
source: spend_key,
target: payment_addr,
token: sdk.native_token(),
amount: InputAmount::from_str("5").expect("Invalid amount"),
};

// build the tx
let mut transfer_tx_builder = sdk
.new_shielded_transfer(
vec![data],
vec![spend_key]
)
.wrapper_fee_payer(alice_acct.public_key.clone());

let (mut transfer_tx, signing_data) = transfer_tx_builder
.build(&sdk)
.await
.expect("Unable to build transfer tx");

// sign the tx
sdk.sign(&mut transfer_tx, &transfer_tx_builder.tx, signing_data, default_sign, ())
.await
.expect("unable to sign transfer tx");

// submit tx to the chain
match sdk.submit(transfer_tx, &transfer_tx_builder.tx).await {
Ok(res) => println!("Tx result: {:?}", res),
Err(e) => println!("Tx error: {:?}", e)
async fn construct_transfer(sdk: impl Namada) {
let spend_key = ExtendedSpendingKey::from_str("zsknam1q0medj45qqqqpq9wh90qd9c7d9f7n5xxn89h6dl54k0jfmucwn4yk7nykxwcrjmk4ylkdnlnn3wkkd9f3ul3nyw8hv5wlsfgklzr5ghzk2spzzwm05csvl2s3rn0aq7f9w4z7guul682yrw4hsmren2k2lgdp003uuj00lsd8nlevc8n32sz6j350up209980c04qdqcu97vh5476xv423k6jp58qn7hqjf9nvsvk8p8g5yfmqguu039q34c2euzhcpnca7vpp4pelqu6y87k")
.expect("Invalid spending key");
let payment_addr = PaymentAddress::from_str("znam1vsz9wsge4u9c0thh38vhn2z9awzfqn4540ue4haxmcxl43srptrgrtlhn6r9cd9razawuakcclc")
.expect("Invalid payment address");

sdk.shielded_mut().await.load().await.expect("Could not load shielded context");

// specify the transfer arguments
let data = TxShieldedTransferData {
source: spend_key,
target: payment_addr,
token: sdk.native_token(),
amount: InputAmount::from_str("5").expect("Invalid amount"),
};

// build the tx
let mut transfer_tx_builder = sdk
.new_shielded_transfer(
vec![data],
vec![spend_key]
)
.wrapper_fee_payer(alice_acct.public_key.clone());

let (mut transfer_tx, signing_data) = transfer_tx_builder
.build(&sdk)
.await
.expect("Unable to build transfer tx");

// sign the tx
sdk.sign(&mut transfer_tx, &transfer_tx_builder.tx, signing_data, default_sign, ())
.await
.expect("unable to sign transfer tx");

// submit tx to the chain
match sdk.submit(transfer_tx, &transfer_tx_builder.tx).await {
Ok(res) => println!("Tx result: {:?}", res),
Err(e) => println!("Tx error: {:?}", e)
}
}
```
16 changes: 8 additions & 8 deletions packages/docs/pages/users/shielded-accounts/shielded-sync.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ necessary to create new notes with the correct zk-proofs. Additionally, it is th
for a client to find out which notes are owned by the user, which is necessary for calculating balances.

Shielded sync is the command that downloads and processes a local view of the MASP (up to
a given block height) and then stores it locally in a `shielded context`. This command must be
a given block height) and then stores it locally in a `shielded wallet`. This command must be
run periodically to stay in sync with the tip of the chain.

## Running shielded sync
Expand All @@ -17,7 +17,7 @@ The simplest way to run shielded sync is via the command:
namadac shielded-sync
```
This assumes several things by default. It will look into the user's wallet and import any MASP
keys into the shielded context. These will be the keys used to attempt to decrypt the downloaded
keys into the shielded wallet. These will be the keys used to attempt to decrypt the downloaded
MASP notes. Additional keys can be provided via:
```bash copy
namadac shielded-sync --spending-keys $SPENDING_KEY
Expand All @@ -28,9 +28,9 @@ namadac shielded-sync --viewing-keys $VIEWING_KEY
```

<Callout>
The first time you wish to check the balance of a spending/viewing key, you must provide it to the shielded context with
The first time you wish to check the balance of a spending/viewing key, you must provide it to the shielded wallet with
the `--spending-keys|--viewing-keys` argument. On subsequent runs, you can simply use `namadac shielded-sync`, as these
keys will be stored in the shielded context.
keys will be stored in the shielded wallet.
</Callout>

Shielded sync also assumes by default that the user wishes to sync to the tip of the chain. If this is not
Expand All @@ -41,7 +41,7 @@ namadac shielded-sync --to-height $BLOCKHEIGHT

## Interrupting

If the shielded context is sufficiently behind the tip of the chain, the amount of data to be downloaded
If the shielded wallet is sufficiently behind the tip of the chain, the amount of data to be downloaded
and the computational load of processing it can be cumbersome. This is one reason why a user might
wish to specify a maximal height to which to sync.

Expand All @@ -63,16 +63,16 @@ The api endpoint `$INDEXER_API_URL` will be a url to the indexer ending with `/a

## Recovering from data corruption

If, for some reason, the shielded context gets corrupted and shielded sync cannot function correctly,
a user can try to remove all local data and sync again from scratch. The shielded context is stored
If, for some reason, the shielded wallet gets corrupted and shielded sync cannot function correctly,
a user can try to remove all local data and sync again from scratch. The shielded wallet is stored
in the user's chain directory and the generated files have the following names:
* `shielded.dat`
* `shielded.tmp`
* `speculative_shielded.dat`
* `shielded_sync.cache`
* `shielded_sync.cache.tmp`

If these files are deleted, shielded sync can be run in order to resync from scratch.
If these files are deleted, shielded sync can be run in order to re-sync from scratch.

<Callout>
Removing or modifying these files is not recommended and should only be done as a last resort.
Expand Down
Loading

0 comments on commit ab03edd

Please sign in to comment.