Skip to content

Commit

Permalink
Add support recover key (#5419)
Browse files Browse the repository at this point in the history
## Description

Add recover-public-key to forc-crypto

This function is ported from
FuelLabs/forc-wallet#152

This PR depends on #5388

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [x] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.
  • Loading branch information
crodas authored Jan 23, 2024
1 parent 161c373 commit a8d76e3
Show file tree
Hide file tree
Showing 13 changed files with 100 additions and 31 deletions.
40 changes: 21 additions & 19 deletions forc-plugins/forc-crypto/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,31 +71,33 @@ fn read_as_binary(content: &Option<String>) -> Vec<u8> {
.unwrap_or_default()
}

/// Reads the arg and returns a vector of bytes
///
/// These are the rules
/// 1. If None, stdin is read.
/// 2. If it's a String and it happens to be a file path, its content will be returned
/// 3. If it's a String and it is "-", stdin is read
/// 4. If the string starts with "0x", it will be treated as a hex string. Only
/// fully valid hex strings are accepted.
/// 5. Otherwise the String will be converted to a vector of bytes
pub fn read_content_filepath_or_stdin(arg: Option<String>) -> Vec<u8> {
match checked_read_file(&arg) {
Some(bytes) => bytes,
None => match checked_read_stdin(&arg, io::stdin().lock()) {
Some(bytes) => bytes,
None => read_as_binary(&arg),
},
}
}

/// The HashArgs takes no or a single argument, it can be either a string or a
/// path to a file. It can be consumed and converted to a Vec<u8> using the From
/// trait.
///
/// The usage is as follows:
/// 1. Zero or one argument is accepted
/// 2. If no argument is passed, `stdin` is being read
/// 3. The argument will be checked to be a file path, if it is the content
/// will be ded from the file
/// 4. Otherwise, the content is treated as a string
/// 5. If the string is "-", `stdin` is being read
/// 6. If the string starts with "0x", it will be treated as a hex string. Only
/// fully valid hex strings are accepted.
/// 7. Any other string, or any malformed hex string will be treated as a
/// vector of bytes
/// This is a wrapper around `read_content_filepath_or_stdin`
impl From<HashArgs> for Vec<u8> {
fn from(value: HashArgs) -> Self {
let arg = value.content_or_filepath;
match checked_read_file(&arg) {
Some(bytes) => bytes,
None => match checked_read_stdin(&arg, io::stdin().lock()) {
Some(bytes) => bytes,
None => read_as_binary(&arg),
},
}
read_content_filepath_or_stdin(value.content_or_filepath)
}
}

Expand Down
67 changes: 67 additions & 0 deletions forc-plugins/forc-crypto/src/keys/get_public_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use crate::args::read_content_filepath_or_stdin;
use anyhow::Result;
use fuel_crypto::{fuel_types::Address, Message, Signature};
use fuels_core::types::bech32::Bech32Address;
use serde_json::json;

forc_util::cli_examples! {
[ Get the public key from a message and its signature => crypto r#"get-public-key \
0x1eff08081394b72239a0cf7ff6b499213dcb7a338bedbd75d072d504588ef27a1f74d5ceb2f111ec02ede097fb09ed00aa9867922ed39299dae0b1afc0fa8661 \
"This is a message that is signed""# ]
}

/// Parse a secret key to view the associated public key
#[derive(Debug, clap::Args)]
#[clap(
author,
version,
about = "Get the public key from a message and its signature",
after_long_help = help(),
)]
pub struct Arg {
/// A private key in hex format
signature: Signature,
/// A message
message: Option<String>,
}

pub fn handler(arg: Arg) -> Result<serde_json::Value> {
let message = Message::new(read_content_filepath_or_stdin(arg.message));
let public_key = Signature::recover(&arg.signature, &message)?;

let bytes = *public_key.hash();

let bech32 = Bech32Address::from(Address::from(bytes));
let addr = Address::from(bytes);

Ok(json!({
"PublicKey": public_key.to_string(),
"Bech32": bech32.to_string(),
"Address": addr.to_string(),
}))
}

#[cfg(test)]
mod test {
use std::str::FromStr;

use super::*;

#[test]
fn expect_output() {
let arg = Arg {
signature: Signature::from_str("0x1eff08081394b72239a0cf7ff6b499213dcb7a338bedbd75d072d504588ef27a1f74d5ceb2f111ec02ede097fb09ed00aa9867922ed39299dae0b1afc0fa8661").unwrap(),
message: Some("This is a message that is signed".to_string()),
};
let json = handler(arg).unwrap();
assert_eq!(
"fuel1fmmfhjapeak3knq96arrvttwrtmzghe0w9gx79gkcl2jhaweakdqfqhzdr",
json.as_object()
.unwrap()
.get("Bech32")
.unwrap()
.as_str()
.unwrap(),
)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use clap::ValueEnum;

pub mod get_public_key;
pub mod new_key;
pub mod parse_secret;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
//! This file will be hosted here until
//! https://github.com/FuelLabs/sway/issues/5170 is fixed
use super::KeyType;
use anyhow::Result;
use fuel_core_types::{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
//! This file will be hosted here until
//! https://github.com/FuelLabs/sway/issues/5170 is fixed
use super::KeyType;
use anyhow::Result;
use fuel_core_types::{fuel_crypto::SecretKey, fuel_tx::Input};
Expand Down
19 changes: 11 additions & 8 deletions forc-plugins/forc-crypto/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,20 @@ use termion::screen::IntoAlternateScreen;
mod address;
mod args;
mod keccak256;
mod keygen;
mod keys;
mod sha256;

const ABOUT: &str = "Forc plugin for hashing arbitrary data.";

fn help() -> &'static str {
Box::leak(
format!(
"EXAMPLES:\n{}{}{}{}",
"EXAMPLES:\n{}{}{}{}{}",
args::examples(),
address::examples(),
keygen::new_key::examples(),
keygen::parse_secret::examples(),
keys::new_key::examples(),
keys::parse_secret::examples(),
keys::get_public_key::examples(),
)
.into_boxed_str(),
)
Expand All @@ -42,8 +43,9 @@ pub enum Command {
Keccak256(args::HashArgs),
Sha256(args::HashArgs),
Address(address::Args),
NewKey(keygen::new_key::Arg),
ParseSecret(keygen::parse_secret::Arg),
GetPublicKey(keys::get_public_key::Arg),
NewKey(keys::new_key::Arg),
ParseSecret(keys::parse_secret::Arg),
}

fn main() {
Expand All @@ -58,10 +60,11 @@ fn run() -> Result<()> {
let app = Command::parse();
let content = match app {
Command::Keccak256(arg) => keccak256::hash(arg)?,
Command::GetPublicKey(arg) => keys::get_public_key::handler(arg)?,
Command::Sha256(arg) => sha256::hash(arg)?,
Command::Address(arg) => address::dump_address(arg.address)?,
Command::NewKey(arg) => keygen::new_key::handler(arg)?,
Command::ParseSecret(arg) => keygen::parse_secret::handler(arg)?,
Command::NewKey(arg) => keys::new_key::handler(arg)?,
Command::ParseSecret(arg) => keys::parse_secret::handler(arg)?,
};

display_output(content)
Expand Down
Empty file modified forc-plugins/forc-debug/README.md
100755 → 100644
Empty file.
Empty file modified forc-plugins/forc-debug/docs/walkthrough.md
100755 → 100644
Empty file.
Empty file modified forc-plugins/forc-debug/examples/client_usage.rs
100755 → 100644
Empty file.
Empty file modified forc-plugins/forc-debug/examples/example_tx.json
100755 → 100644
Empty file.
Empty file modified forc-plugins/forc-debug/src/lib.rs
100755 → 100644
Empty file.
Empty file modified forc-plugins/forc-debug/src/names.rs
100755 → 100644
Empty file.
Empty file modified forc-plugins/forc-debug/tests/cli_integration.rs
100755 → 100644
Empty file.

0 comments on commit a8d76e3

Please sign in to comment.