Refactor SignerSource to expose DerivationPath to other kinds of signers (#16933)

* One use statement

* Add stdin uri scheme

* Convert parse_signer_source to return Result

* A-Z deps

* Convert Usb data to Locator

* Pull DerivationPath out of Locator

* Wrap SignerSource to share derivation_path

* Review comments

* Check Filepath existence, readability in parse_signer_source
This commit is contained in:
Tyera Eulberg
2021-04-29 01:42:21 -06:00
committed by GitHub
parent d640ac143b
commit d6f30b7537
15 changed files with 509 additions and 447 deletions

View File

@ -44,36 +44,38 @@ byteorder = { version = "1.3.4", optional = true }
chrono = { version = "0.4", optional = true }
curve25519-dalek = { version = "2.1.0", optional = true }
derivation-path = { version = "0.1.3", default-features = false }
digest = { version = "0.9.0", optional = true }
ed25519-dalek = { version = "=1.0.1", optional = true }
generic-array = { version = "0.14.3", default-features = false, features = ["serde", "more_lengths"], optional = true }
hex = "0.4.2"
hmac = "0.10.1"
itertools = "0.9.0"
lazy_static = "1.4.0"
libsecp256k1 = { version = "0.3.5", optional = true }
log = "0.4.11"
memmap2 = { version = "0.1.0", optional = true }
num-derive = "0.3"
num-traits = "0.2"
pbkdf2 = { version = "0.6.0", default-features = false }
qstring = "0.7.2"
rand = { version = "0.7.0", optional = true }
rand_chacha = { version = "0.2.2", optional = true }
rand_core = "0.6.2"
rustversion = "1.0.4"
serde = "1.0.122"
serde_bytes = "0.11"
serde_derive = "1.0.103"
serde_json = { version = "1.0.56", optional = true }
sha2 = "0.9.2"
thiserror = "1.0"
ed25519-dalek = { version = "=1.0.1", optional = true }
sha3 = { version = "0.9.1", optional = true }
solana-crate-features = { path = "../crate-features", version = "=1.7.0", optional = true }
solana-logger = { path = "../logger", version = "=1.7.0", optional = true }
solana-frozen-abi = { path = "../frozen-abi", version = "=1.7.0" }
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.7.0" }
solana-program = { path = "program", version = "=1.7.0" }
solana-sdk-macro = { path = "macro", version = "=1.7.0" }
rustversion = "1.0.4"
libsecp256k1 = { version = "0.3.5", optional = true }
sha3 = { version = "0.9.1", optional = true }
digest = { version = "0.9.0", optional = true }
thiserror = "1.0"
uriparse = "0.6.3"
[dev-dependencies]
curve25519-dalek = "2.1.0"

View File

@ -7,6 +7,7 @@ use {
str::FromStr,
},
thiserror::Error,
uriparse::URIReference,
};
const ACCOUNT_INDEX: usize = 2;
@ -109,6 +110,7 @@ impl DerivationPath {
self.0.path()
}
// Assumes `key` query-string key
pub fn get_query(&self) -> String {
if let Some(account) = &self.account() {
if let Some(change) = &self.change() {
@ -120,6 +122,34 @@ impl DerivationPath {
"".to_string()
}
}
// Only accepts single query string pair of type `key`
pub fn from_uri(uri: &URIReference<'_>) -> Result<Option<Self>, DerivationPathError> {
if let Some(query) = uri.query() {
let query_str = query.as_str();
if query_str.is_empty() {
return Ok(None);
}
let query = qstring::QString::from(query_str);
if query.len() > 1 {
return Err(DerivationPathError::InvalidDerivationPath(
"invalid query string, extra fields not supported".to_string(),
));
}
let key = query.get("key");
if key.is_none() {
return Err(DerivationPathError::InvalidDerivationPath(format!(
"invalid query string `{}`, only `key` supported",
query_str,
)));
}
// Use from_key_str instead of TryInto here to make it a little more explicit that this
// generates a Solana bip44 DerivationPath
key.map(Self::from_key_str).transpose()
} else {
Ok(None)
}
}
}
impl fmt::Debug for DerivationPath {
@ -161,6 +191,7 @@ impl Bip44 for Solana {
#[cfg(test)]
mod tests {
use super::*;
use uriparse::URIReferenceBuilder;
struct TestCoin;
impl Bip44 for TestCoin {
@ -263,6 +294,190 @@ mod tests {
);
}
#[test]
fn test_new_from_uri() {
let derivation_path = DerivationPath::new_bip44(Some(0), Some(0));
// test://path?key=0/0
let mut builder = URIReferenceBuilder::new();
builder
.try_scheme(Some("test"))
.unwrap()
.try_authority(Some("path"))
.unwrap()
.try_path("")
.unwrap()
.try_query(Some("key=0/0"))
.unwrap();
let uri = builder.build().unwrap();
assert_eq!(
DerivationPath::from_uri(&uri).unwrap(),
Some(derivation_path.clone())
);
// test://path?key=0'/0'
let mut builder = URIReferenceBuilder::new();
builder
.try_scheme(Some("test"))
.unwrap()
.try_authority(Some("path"))
.unwrap()
.try_path("")
.unwrap()
.try_query(Some("key=0'/0'"))
.unwrap();
let uri = builder.build().unwrap();
assert_eq!(
DerivationPath::from_uri(&uri).unwrap(),
Some(derivation_path.clone())
);
// test://path?key=0\'/0\'
let mut builder = URIReferenceBuilder::new();
builder
.try_scheme(Some("test"))
.unwrap()
.try_authority(Some("path"))
.unwrap()
.try_path("")
.unwrap()
.try_query(Some("key=0\'/0\'"))
.unwrap();
let uri = builder.build().unwrap();
assert_eq!(
DerivationPath::from_uri(&uri).unwrap(),
Some(derivation_path)
);
// test://path
let mut builder = URIReferenceBuilder::new();
builder
.try_scheme(Some("test"))
.unwrap()
.try_authority(Some("path"))
.unwrap()
.try_path("")
.unwrap();
let uri = builder.build().unwrap();
assert_eq!(DerivationPath::from_uri(&uri).unwrap(), None);
// test://path?
let mut builder = URIReferenceBuilder::new();
builder
.try_scheme(Some("test"))
.unwrap()
.try_authority(Some("path"))
.unwrap()
.try_path("")
.unwrap()
.try_query(Some(""))
.unwrap();
let uri = builder.build().unwrap();
assert_eq!(DerivationPath::from_uri(&uri).unwrap(), None);
// test://path?key=0/0/0
let mut builder = URIReferenceBuilder::new();
builder
.try_scheme(Some("test"))
.unwrap()
.try_authority(Some("path"))
.unwrap()
.try_path("")
.unwrap()
.try_query(Some("key=0/0/0"))
.unwrap();
let uri = builder.build().unwrap();
assert!(matches!(
DerivationPath::from_uri(&uri),
Err(DerivationPathError::InvalidDerivationPath(_))
));
// test://path?key=0/0&bad-key=0/0
let mut builder = URIReferenceBuilder::new();
builder
.try_scheme(Some("test"))
.unwrap()
.try_authority(Some("path"))
.unwrap()
.try_path("")
.unwrap()
.try_query(Some("key=0/0&bad-key=0/0"))
.unwrap();
let uri = builder.build().unwrap();
assert!(matches!(
DerivationPath::from_uri(&uri),
Err(DerivationPathError::InvalidDerivationPath(_))
));
// test://path?bad-key=0/0
let mut builder = URIReferenceBuilder::new();
builder
.try_scheme(Some("test"))
.unwrap()
.try_authority(Some("path"))
.unwrap()
.try_path("")
.unwrap()
.try_query(Some("bad-key=0/0"))
.unwrap();
let uri = builder.build().unwrap();
assert!(matches!(
DerivationPath::from_uri(&uri),
Err(DerivationPathError::InvalidDerivationPath(_))
));
// test://path?key=bad-value
let mut builder = URIReferenceBuilder::new();
builder
.try_scheme(Some("test"))
.unwrap()
.try_authority(Some("path"))
.unwrap()
.try_path("")
.unwrap()
.try_query(Some("key=bad-value"))
.unwrap();
let uri = builder.build().unwrap();
assert!(matches!(
DerivationPath::from_uri(&uri),
Err(DerivationPathError::InvalidDerivationPath(_))
));
// test://path?key=
let mut builder = URIReferenceBuilder::new();
builder
.try_scheme(Some("test"))
.unwrap()
.try_authority(Some("path"))
.unwrap()
.try_path("")
.unwrap()
.try_query(Some("key="))
.unwrap();
let uri = builder.build().unwrap();
assert!(matches!(
DerivationPath::from_uri(&uri),
Err(DerivationPathError::InvalidDerivationPath(_))
));
// test://path?key
let mut builder = URIReferenceBuilder::new();
builder
.try_scheme(Some("test"))
.unwrap()
.try_authority(Some("path"))
.unwrap()
.try_path("")
.unwrap()
.try_query(Some("key"))
.unwrap();
let uri = builder.build().unwrap();
assert!(matches!(
DerivationPath::from_uri(&uri),
Err(DerivationPathError::InvalidDerivationPath(_))
));
}
#[test]
fn test_get_query() {
let derivation_path = DerivationPath::new_bip44_with_coin(TestCoin, None, None);