sign_plc_operation: new_plc_did

+1
.gitignore
···
target/
cli/history.txt
+
cli/operation.json
+9
Cargo.lock
···
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
+
name = "base32"
+
version = "0.5.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076"
+
+
[[package]]
name = "base58"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
···
name = "vnd-atproto-cli"
version = "0.0.1"
dependencies = [
+
"base32",
"base58",
"base64",
"clap",
···
"ledger-transport-hid",
"rustyline",
"serde",
+
"serde_ipld_dagcbor",
"serde_json",
+
"sha2",
"shellwords",
"tokio",
"vnd-atproto-client",
+3
cli/Cargo.toml
···
[dependencies]
base58 = "0.2.0"
+
base32 = "0.5.1"
base64 = "0.22.1"
+
sha2 = "0.10.8"
client = { package = "vnd-atproto-client", path = "../client"}
clap = { version = "4.5.31", features = ["derive"] }
rustyline = "15.0.0"
shellwords = "1.1.0"
hidapi = "2.6.3"
ledger-transport-hid = "0.11.0"
+
serde_ipld_dagcbor = { git = "http://github.com/edouardparis/serde_ipld_dagcbor", branch = "scopeguard-no-default-features", default-features = false }
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
tokio = { version = "1.38.1", features = ["io-util", "macros", "net", "rt", "rt-multi-thread", "sync"] }
-19
cli/operation.json
···
-
{
-
"type": "plc_operation",
-
"services": {
-
"atproto_pds": {
-
"type": "AtprotoPersonalDataServer",
-
"endpoint": "https://bsky.social"
-
}
-
},
-
"alsoKnownAs": [
-
"at://atproto.com"
-
],
-
"rotationKeys": [
-
"did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg",
-
"did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK"
-
],
-
"verificationMethods": {
-
"atproto": "did:key:zQ3shXjHeiBuRCKmM36cuYnm7YEMzhGnCmCyW92sRJ9pribSF"
-
}
-
}
+22 -7
cli/src/main.rs
···
use rustyline::hint::Hinter;
use rustyline::validate::{ValidationContext, ValidationResult, Validator};
use rustyline::{Context, Editor, Helper};
+
use sha2::Digest;
use hidapi::HidApi;
use ledger_transport_hid::TransportNativeHID;
···
AtprotoAppClient,
};
-
use serde::Deserialize;
+
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fs::File;
use std::io::Read;
···
/// Path to previous operation json file
#[clap(long)]
previous: Option<String>,
+
#[clap(long, default_missing_value = "true", num_args = 0..=1)]
+
new_plc_did: bool,
},
Exit,
}
···
key_index,
operation,
previous,
+
new_plc_did,
} => {
let operation = read_operation_file(&operation)?;
let previous = if let Some(path) = previous {
···
None
};
let sig = app_client
-
.sign_plc_operation(key_index.unwrap_or(0), operation, previous)
-
.await?;
-
println!(
-
"sig: {}",
-
base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(sig)
-
);
+
.sign_plc_operation(key_index.unwrap_or(0), operation.clone(), previous)
+
.await
+
.map(|s| base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(s))?;
+
println!("sig: {}", sig);
+
if *new_plc_did {
+
let b = serde_ipld_dagcbor::to_vec(&SignedPlcOperation { operation, sig }).unwrap();
+
let hash = sha2::Sha256::digest(b);
+
let s = base32::encode(base32::Alphabet::Rfc4648Lower { padding: true }, &hash);
+
eprintln!("did:plc:{}", &s[..24]);
+
}
}
CliCommand::Exit => {
app_client.exit().await?;
···
}
}
Ok(())
+
}
+
+
#[derive(Serialize)]
+
pub struct SignedPlcOperation {
+
#[serde(flatten)]
+
operation: client::PlcOperation,
+
sig: String,
}
fn read_operation_file(path: &str) -> Result<client::PlcOperation, Box<dyn std::error::Error>> {