Skip to content

Commit 2bf2ed7

Browse files
Merge #90
90: feat: add nilauth interaction commands to `nillion` cli r=mfontanini a=mfontanini This adds a few commands to interact with nilauth to the `nillion` cli. e.g. now you can `nillion nilauth subscription pay` and `nillion nilauth revoke <token>` and it will do what you expect. This makes it easier to test things and play around with nilauth. For this I added a new `nilauth` entry in the networks config file that is optional and currently needs to be filled in by hand. I did change the devnet to not overwrite this field if it already exists so we can edit the devnet config file, start the devnet, and not lose the parameters. Co-authored-by: Matias Fontanini <[email protected]>
2 parents a2887f9 + 6c5a776 commit 2bf2ed7

File tree

8 files changed

+205
-16
lines changed

8 files changed

+205
-16
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/libs/tools-config/src/client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub struct ClientParameters {
2626
impl ClientParameters {
2727
pub async fn try_build(self) -> anyhow::Result<VmClient> {
2828
let ClientParameters { identity, network } = self;
29-
let NetworkConfig { bootnode, payments } = NetworkConfig::read_from_config(&network)?;
29+
let NetworkConfig { bootnode, payments, .. } = NetworkConfig::read_from_config(&network)?;
3030
let Identity { private_key: user_key, kind: curve } = Identity::read_from_config(&identity)?;
3131
let user_key = user_key.try_into().map_err(|_| anyhow!("invalid user key length"))?;
3232
let user_key = match curve {

tools/libs/tools-config/src/networks.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ pub struct NetworkConfig {
1212

1313
/// Payments configuration
1414
pub payments: Option<PaymentsConfig>,
15+
16+
/// The nilauth configuration.
17+
pub nilauth: Option<NilauthConfig>,
1518
}
1619

1720
#[derive(Default, Serialize, Deserialize, Debug, PartialEq)]
@@ -39,3 +42,9 @@ impl ToolConfig for NetworkConfig {
3942
config_directory().map(|dir| dir.join("networks")).unwrap_or_else(|| PathBuf::from("./"))
4043
}
4144
}
45+
46+
#[derive(Serialize, Deserialize, Debug, Default)]
47+
pub struct NilauthConfig {
48+
/// The nilauth endpoint to use.
49+
pub endpoint: String,
50+
}

tools/nillion-devnet/src/cluster.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ impl ClusterOrchestrator {
217217
private_keys: &[String],
218218
) -> Result<()> {
219219
let private_key = private_keys.first().ok_or_else(|| anyhow!("no private keys")).cloned()?;
220-
let network_config = tools_config::networks::NetworkConfig {
220+
let mut network_config = tools_config::networks::NetworkConfig {
221221
bootnode: bootnode_address,
222222
payments: Some(PaymentsConfig {
223223
nilchain_chain_id: Some(NILCHAIN_CHAIN_ID.to_string()),
@@ -226,7 +226,12 @@ impl ClusterOrchestrator {
226226
nilchain_private_key: private_key,
227227
gas_price: None,
228228
}),
229+
nilauth: None,
229230
};
231+
// Move over any existing nilauth config, if any, so we don't lose it across re-runs
232+
if let Ok(config) = tools_config::networks::NetworkConfig::read_from_config("devnet") {
233+
network_config.nilauth = config.nilauth;
234+
}
230235
network_config.write_to_file("devnet")?;
231236

232237
println!(

tools/nillion/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ hex = { version = "0.4", features = ["serde"] }
1313
futures = "0.3.30"
1414
log = "0.4"
1515
nillion-nucs = { path = "../../libs/nucs" }
16+
nilauth-client = { path = "../../libs/nilauth-client" }
1617
serde = "1.0.214"
1718
serde_yaml = "0.9"
1819
serde_json = "1.0.132"

tools/nillion/src/args.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ pub enum Command {
100100
/// Mint or inspect a NUC.
101101
#[clap(subcommand)]
102102
Nuc(NucCommand),
103+
104+
/// Interact with nilauth.
105+
#[clap(subcommand)]
106+
Nilauth(NilauthCommand),
103107
}
104108

105109
/// The output format for the command. Default is YAML.
@@ -227,6 +231,10 @@ pub struct AddNetworkArgs {
227231
/// The gas price to use in nilchain transactions.
228232
#[arg(long, requires = "nilchain_rpc_endpoint")]
229233
pub nilchain_gas_price: Option<f64>,
234+
235+
/// The nilauth endpoint.
236+
#[arg(long)]
237+
pub nilauth_endpoint: Option<String>,
230238
}
231239

232240
/// The arguments for the network edit command.
@@ -574,6 +582,47 @@ pub struct ValidateNucArgs {
574582
pub root_public_keys: Vec<HexBytes>,
575583
}
576584

585+
/// A nilauth command.
586+
#[derive(Subcommand)]
587+
pub enum NilauthCommand {
588+
/// Manage nilauth subscription.
589+
#[clap(subcommand)]
590+
Subscription(NilauthSubscriptionCommand),
591+
592+
/// Request a token.
593+
Token,
594+
595+
/// Revoke a token.
596+
Revoke(RevokeTokenArgs),
597+
598+
/// Check whether a token is revoked.
599+
CheckRevoked(CheckRevokedArgs),
600+
}
601+
602+
/// A nilauth subscription command.
603+
#[derive(Subcommand)]
604+
pub enum NilauthSubscriptionCommand {
605+
/// Pay for a subscription.
606+
Pay,
607+
608+
/// Get the subscription status.
609+
Status,
610+
}
611+
612+
/// The arguments to a revoke token command.
613+
#[derive(Args)]
614+
pub struct RevokeTokenArgs {
615+
/// The token to revoke.
616+
pub token: String,
617+
}
618+
619+
/// The arguments to a check revoked command.
620+
#[derive(Args)]
621+
pub struct CheckRevokedArgs {
622+
/// The token to check for.
623+
pub token: String,
624+
}
625+
577626
/// A vec that can be parsed from hex.
578627
#[derive(Clone, Debug)]
579628
pub struct HexBytes(pub(crate) Vec<u8>);

tools/nillion/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ async fn run(cli: Cli) -> Result<Box<dyn SerializeAsAny>> {
6363
ClientParameters { identity, network }
6464
}
6565
};
66-
let client = parameters.try_build().await.context("failed to create client")?;
67-
let cli_runner = Runner::new(client);
66+
let client = parameters.clone().try_build().await.context("failed to create client")?;
67+
let cli_runner = Runner::new(client, parameters);
6868
cli_runner.run(command).await
6969
}
7070

tools/nillion/src/runner.rs

Lines changed: 136 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use crate::{
22
args::{
3-
AddFundsArgs, AddIdentityArgs, AddNetworkArgs, BalanceCommand, Cli, ClusterConfigArgs, Command, ComputeArgs,
4-
ConfigCommand, DeleteValuesArgs, EditIdentityArgs, EditNetworkArgs, IdentityGenArgs, InspectNucArgs,
5-
OverwritePermissionsArgs, PreprocessingPoolStatusArgs, RemoveIdentityArgs, RemoveNetworkArgs,
6-
RetrievePermissionsArgs, RetrieveValuesArgs, ShowIdentityArgs, ShowNetworkArgs, StoreProgramArgs,
7-
StoreValuesArgs, UpdatePermissionsArgs, UseContextArgs, ValidateNucArgs,
3+
AddFundsArgs, AddIdentityArgs, AddNetworkArgs, BalanceCommand, CheckRevokedArgs, Cli, ClusterConfigArgs,
4+
Command, ComputeArgs, ConfigCommand, DeleteValuesArgs, EditIdentityArgs, EditNetworkArgs, IdentityGenArgs,
5+
InspectNucArgs, NilauthCommand, NilauthSubscriptionCommand, OverwritePermissionsArgs,
6+
PreprocessingPoolStatusArgs, RemoveIdentityArgs, RemoveNetworkArgs, RetrievePermissionsArgs,
7+
RetrieveValuesArgs, RevokeTokenArgs, ShowIdentityArgs, ShowNetworkArgs, StoreProgramArgs, StoreValuesArgs,
8+
UpdatePermissionsArgs, UseContextArgs, ValidateNucArgs,
89
},
910
context::ContextConfig,
1011
parse_input_file,
@@ -16,16 +17,17 @@ use chrono::{DateTime, Utc};
1617
use clap::{error::ErrorKind, CommandFactory};
1718
use clap_utils::shell_completions::{handle_shell_completions, ShellCompletionsArgs};
1819
use log::{debug, info};
20+
use nilauth_client::client::{DefaultNilauthClient, NilauthClient};
1921
use nillion_client::{
2022
grpc::payments::AccountBalanceResponse,
2123
operation::{InitialState, PaidOperation, PaidVmOperation},
22-
payments::TxHash,
24+
payments::{NillionChainClient, NillionChainPrivateKey, TxHash},
2325
vm::VmClient,
2426
Ed25519SigningKey, Secp256k1SigningKey, TokenAmount, UserId,
2527
};
2628
use nillion_nucs::{
2729
envelope::{DecodedNucToken, NucTokenEnvelope},
28-
k256,
30+
k256::{self, SecretKey},
2931
token::{NucToken, ProofHash},
3032
validator::NucValidator,
3133
};
@@ -39,20 +41,22 @@ use std::{
3941
path::Path,
4042
};
4143
use tools_config::{
44+
client::ClientParameters,
4245
identities::{Identity, Kind},
43-
networks::{NetworkConfig, PaymentsConfig},
46+
networks::{NetworkConfig, NilauthConfig, PaymentsConfig},
4447
NamedConfig, ToolConfig,
4548
};
4649
use user_keypair::SigningKey;
4750
use uuid::Uuid;
4851

4952
pub struct Runner {
5053
client: VmClient,
54+
parameters: ClientParameters,
5155
}
5256

5357
impl Runner {
54-
pub fn new(client: VmClient) -> Self {
55-
Self { client }
58+
pub fn new(client: VmClient, parameters: ClientParameters) -> Self {
59+
Self { client, parameters }
5660
}
5761

5862
/// run a command
@@ -74,6 +78,7 @@ impl Runner {
7478
Command::Balance(BalanceCommand::AddFunds(args)) => self.add_funds(args).await,
7579
Command::Config(ConfigCommand::Payments) => self.payments_config().await,
7680
Command::Config(ConfigCommand::Cluster(args)) => self.cluster_config(args).await,
81+
Command::Nilauth(cmd) => self.handle_nilauth(cmd).await,
7782
Command::IdentityGen(_)
7883
| Command::Identities(_)
7984
| Command::Networks(_)
@@ -450,6 +455,7 @@ impl Runner {
450455
nilchain_private_key,
451456
nilchain_chain_id,
452457
nilchain_gas_price,
458+
nilauth_endpoint,
453459
} = args;
454460
let payments = nilchain_rpc_endpoint.map(|nilchain_rpc_endpoint| PaymentsConfig {
455461
nilchain_chain_id,
@@ -459,7 +465,8 @@ impl Runner {
459465
nilchain_private_key: nilchain_private_key.expect("private key not set"),
460466
gas_price: nilchain_gas_price,
461467
});
462-
NetworkConfig { bootnode, payments }.write_to_file(&name)?;
468+
let nilauth = nilauth_endpoint.map(|endpoint| NilauthConfig { endpoint });
469+
NetworkConfig { bootnode, payments, nilauth }.write_to_file(&name)?;
463470
Ok(Box::new(format!("Network {} added", name)))
464471
}
465472

@@ -487,7 +494,7 @@ impl Runner {
487494

488495
pub fn show_network(args: ShowNetworkArgs) -> Result<Box<dyn SerializeAsAny>> {
489496
let config = NetworkConfig::read_from_config(&args.name)?;
490-
let NetworkConfig { bootnode, payments } = config;
497+
let NetworkConfig { bootnode, payments, nilauth } = config;
491498
let mut output = BTreeMap::from([("bootnode", bootnode)]);
492499

493500
if let Some(payments) = payments {
@@ -517,6 +524,10 @@ impl Runner {
517524
output.insert("nilchain_gas_price", gas_price.to_string());
518525
}
519526
}
527+
if let Some(nilauth) = nilauth {
528+
let NilauthConfig { endpoint } = nilauth;
529+
output.insert("nilauth_endpoint", endpoint);
530+
}
520531
Ok(Box::new(output))
521532
}
522533

@@ -701,6 +712,119 @@ impl Runner {
701712
Ok(Box::new(info))
702713
}
703714

715+
async fn handle_nilauth(&self, cmd: NilauthCommand) -> Result<Box<dyn SerializeAsAny>> {
716+
let identity = Identity::read_from_config(&self.parameters.identity)?;
717+
let key = match identity.kind {
718+
Kind::Secp256k1 => SecretKey::from_slice(&identity.private_key)?,
719+
Kind::Ed25519 => bail!("ed25519 not supported"),
720+
};
721+
let config = NetworkConfig::read_from_config(&self.parameters.network)?;
722+
let nilauth = config.nilauth.ok_or_else(|| anyhow!("no nilauth config"))?;
723+
let client = DefaultNilauthClient::new(nilauth.endpoint)?;
724+
match cmd {
725+
NilauthCommand::Subscription(NilauthSubscriptionCommand::Pay) => {
726+
self.pay_nilauth_subscription(&client, config.payments, key).await
727+
}
728+
NilauthCommand::Subscription(NilauthSubscriptionCommand::Status) => {
729+
self.nilauth_subscription_status(&client, key).await
730+
}
731+
NilauthCommand::Token => self.nilauth_request_token(&client, key).await,
732+
NilauthCommand::Revoke(args) => self.nilauth_revoke_token(&client, key, args).await,
733+
NilauthCommand::CheckRevoked(args) => self.nilauth_check_revoked(&client, args).await,
734+
}
735+
}
736+
737+
async fn pay_nilauth_subscription(
738+
&self,
739+
nilauth_client: &dyn NilauthClient,
740+
nilchain_config: Option<PaymentsConfig>,
741+
key: SecretKey,
742+
) -> Result<Box<dyn SerializeAsAny>> {
743+
#[derive(Serialize)]
744+
struct Output {
745+
tx_hash: String,
746+
}
747+
748+
let payments = nilchain_config.ok_or_else(|| anyhow!("no payments config"))?;
749+
let nilchain_key =
750+
NillionChainPrivateKey::from_hex(&payments.nilchain_private_key).context("invalid payments private key")?;
751+
let mut nilchain_client = NillionChainClient::new(payments.nilchain_rpc_endpoint, nilchain_key)
752+
.await
753+
.context("creating nilchain client")?;
754+
if let Some(gas_price) = payments.gas_price {
755+
nilchain_client.set_gas_price(gas_price);
756+
}
757+
758+
let tx_hash = nilauth_client.pay_subscription(&mut nilchain_client, &key.public_key()).await?;
759+
Ok(Box::new(Output { tx_hash: tx_hash.to_string() }))
760+
}
761+
762+
async fn nilauth_subscription_status(
763+
&self,
764+
nilauth_client: &dyn NilauthClient,
765+
key: SecretKey,
766+
) -> Result<Box<dyn SerializeAsAny>> {
767+
#[derive(Serialize)]
768+
struct Output {
769+
subscribed: bool,
770+
771+
#[serde(skip_serializing_if = "Option::is_none")]
772+
expires_at: Option<DateTime<Utc>>,
773+
}
774+
775+
let subscription = nilauth_client.subscription_status(&key).await?;
776+
let output =
777+
Output { subscribed: subscription.subscribed, expires_at: subscription.details.map(|s| s.expires_at) };
778+
Ok(Box::new(output))
779+
}
780+
781+
async fn nilauth_request_token(
782+
&self,
783+
nilauth_client: &dyn NilauthClient,
784+
key: SecretKey,
785+
) -> Result<Box<dyn SerializeAsAny>> {
786+
#[derive(Serialize)]
787+
struct Output {
788+
token: String,
789+
}
790+
791+
let token = nilauth_client.request_token(&key).await?;
792+
Ok(Box::new(Output { token }))
793+
}
794+
795+
async fn nilauth_revoke_token(
796+
&self,
797+
nilauth_client: &dyn NilauthClient,
798+
key: SecretKey,
799+
args: RevokeTokenArgs,
800+
) -> Result<Box<dyn SerializeAsAny>> {
801+
let token = NucTokenEnvelope::decode(&args.token)?;
802+
nilauth_client.revoke_token(&token, &key).await?;
803+
Ok(Box::new("Token revoked".to_string()))
804+
}
805+
806+
async fn nilauth_check_revoked(
807+
&self,
808+
nilauth_client: &dyn NilauthClient,
809+
args: CheckRevokedArgs,
810+
) -> Result<Box<dyn SerializeAsAny>> {
811+
#[derive(Serialize)]
812+
struct Token {
813+
hash: ProofHash,
814+
revoked_at: DateTime<Utc>,
815+
}
816+
817+
#[derive(Serialize)]
818+
struct Output {
819+
tokens: Vec<Token>,
820+
}
821+
822+
let token = NucTokenEnvelope::decode(&args.token)?;
823+
let tokens = nilauth_client.lookup_revoked_tokens(&token).await?;
824+
let tokens = tokens.into_iter().map(|t| Token { hash: t.token_hash, revoked_at: t.revoked_at }).collect();
825+
Ok(Box::new(Output { tokens }))
826+
}
827+
704828
async fn serialize_quote<'a, O: PaidVmOperation>(
705829
&self,
706830
operation: PaidOperation<'a, O, InitialState>,

0 commit comments

Comments
 (0)