Skip to content

Commit

Permalink
Add did:pkh:doge for Dogecoin addresses
Browse files Browse the repository at this point in the history
Check address prefixes
  • Loading branch information
clehner committed Mar 22, 2021
1 parent e52ead1 commit 705a319
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 7 deletions.
73 changes: 72 additions & 1 deletion did-pkh/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ use ssi::did_resolve::{
};
use ssi::jwk::{Base64urlUInt, OctetParams, Params, JWK};

// https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-4.md
const CHAIN_ID_BITCOIN_MAINNET: &str = "bip122:000000000019d6689c085ae165831e93";
const CHAIN_ID_DOGECOIN_MAINNET: &str = "bip122:1a91e3dace36e2be3bf030a65679fe82";

/// did:pkh DID Method
pub struct DIDPKH;
Expand Down Expand Up @@ -202,6 +204,9 @@ async fn resolve_sol(did: &str, account_address: String) -> ResolutionResult {
}

async fn resolve_btc(did: &str, account_address: String) -> ResolutionResult {
if !account_address.starts_with("1") {
return resolution_error(&ERROR_INVALID_DID);
};
let blockchain_account_id = BlockchainAccountId {
account_address,
chain_id: CHAIN_ID_BITCOIN_MAINNET.to_string(),
Expand Down Expand Up @@ -229,6 +234,37 @@ async fn resolve_btc(did: &str, account_address: String) -> ResolutionResult {
resolution_result(doc)
}

async fn resolve_doge(did: &str, account_address: String) -> ResolutionResult {
if !account_address.starts_with("D") {
return resolution_error(&ERROR_INVALID_DID);
}
let blockchain_account_id = BlockchainAccountId {
account_address,
chain_id: CHAIN_ID_DOGECOIN_MAINNET.to_string(),
};
let vm_url = DIDURL {
did: did.to_string(),
fragment: Some("blockchainAccountId".to_string()),
..Default::default()
};
let vm = VerificationMethod::Map(VerificationMethodMap {
id: String::from(vm_url.clone()),
type_: "EcdsaSecp256k1RecoveryMethod2020".to_string(),
controller: did.to_string(),
blockchain_account_id: Some(blockchain_account_id.to_string()),
..Default::default()
});
let doc = Document {
context: Contexts::One(Context::URI(DEFAULT_CONTEXT.to_string())),
id: did.to_string(),
verification_method: Some(vec![vm]),
authentication: Some(vec![VerificationMethod::DIDURL(vm_url.clone())]),
assertion_method: Some(vec![VerificationMethod::DIDURL(vm_url)]),
..Default::default()
};
resolution_result(doc)
}

#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl DIDResolver for DIDPKH {
Expand All @@ -247,6 +283,7 @@ impl DIDResolver for DIDPKH {
"eth" => resolve_eth(did, data).await,
"sol" => resolve_sol(did, data).await,
"btc" => resolve_btc(did, data).await,
"doge" => resolve_doge(did, data).await,
_ => resolution_error(&ERROR_INVALID_DID),
}
}
Expand All @@ -265,6 +302,24 @@ fn generate_sol(jwk: &JWK) -> Option<String> {
}
}

fn generate_btc(key: &JWK) -> Result<String, String> {
let addr = ssi::ripemd::hash_public_key(key, 0x00)?;
#[cfg(test)]
if !addr.starts_with("1") {
return Err("Expected Bitcoin address".to_string());
}
Ok(addr)
}

fn generate_doge(key: &JWK) -> Result<String, String> {
let addr = ssi::ripemd::hash_public_key(key, 0x1e)?;
#[cfg(test)]
if !addr.starts_with("D") {
return Err("Expected Dogecoin address".to_string());
}
Ok(addr)
}

impl DIDMethod for DIDPKH {
fn name(&self) -> &'static str {
return "pkh";
Expand All @@ -279,7 +334,8 @@ impl DIDMethod for DIDPKH {
"tz" => ssi::blakesig::hash_public_key(key).ok(),
"eth" => ssi::keccak_hash::hash_public_key(key).ok(),
"sol" => generate_sol(key),
"btc" => ssi::ripemd::hash_public_key(key).ok(),
"btc" => generate_btc(key).ok(),
"doge" => generate_doge(key).ok(),
_ => None,
} {
Some(addr) => addr,
Expand Down Expand Up @@ -406,6 +462,11 @@ mod tests {
include_str!("../tests/did-btc.jsonld"),
)
.await;
test_resolve(
"did:pkh:doge:DH5yaieqoZN36fDVciNyRueRGvGLR3mr7L",
include_str!("../tests/did-doge.jsonld"),
)
.await;
test_resolve_error("did:pkh:tz:foo", ERROR_INVALID_DID).await;
test_resolve_error("did:pkh:eth:bar", ERROR_INVALID_DID).await;
}
Expand Down Expand Up @@ -640,5 +701,15 @@ mod tests {
&ssi::ldp::EcdsaSecp256k1RecoverySignature2020,
)
.await;

println!("did:pkh:doge");
credential_prove_verify_did_pkh(
key_secp256k1_recovery.clone(),
other_key_secp256k1.clone(),
"doge",
"#blockchainAccountId",
&ssi::ldp::EcdsaSecp256k1RecoverySignature2020,
)
.await;
}
}
18 changes: 18 additions & 0 deletions did-pkh/tests/did-doge.jsonld
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"@context": "https://www.w3.org/ns/did/v1",
"id": "did:pkh:doge:DH5yaieqoZN36fDVciNyRueRGvGLR3mr7L",
"verificationMethod": [
{
"id": "did:pkh:doge:DH5yaieqoZN36fDVciNyRueRGvGLR3mr7L#blockchainAccountId",
"type": "EcdsaSecp256k1RecoveryMethod2020",
"controller": "did:pkh:doge:DH5yaieqoZN36fDVciNyRueRGvGLR3mr7L",
"blockchainAccountId": "DH5yaieqoZN36fDVciNyRueRGvGLR3mr7L@bip122:1a91e3dace36e2be3bf030a65679fe82"
}
],
"authentication": [
"did:pkh:doge:DH5yaieqoZN36fDVciNyRueRGvGLR3mr7L#blockchainAccountId"
],
"assertionMethod": [
"did:pkh:doge:DH5yaieqoZN36fDVciNyRueRGvGLR3mr7L#blockchainAccountId"
]
}
13 changes: 11 additions & 2 deletions src/caip10.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,18 @@ impl BlockchainAccountId {
.map_err(|e| BlockchainAccountIdVerifyError::HashError(e.to_string())),
["solana"] => encode_ed25519(&jwk)
.map_err(|e| BlockchainAccountIdVerifyError::HashError(e.to_string())),
// Bitcoin
#[cfg(feature = "ripemd160")]
["bip122", "000000000019d6689c085ae165831e93"] => crate::ripemd::hash_public_key(&jwk)
.map_err(|e| BlockchainAccountIdVerifyError::HashError(e.to_string())),
["bip122", "000000000019d6689c085ae165831e93"] => {
crate::ripemd::hash_public_key(&jwk, 0x00)
.map_err(|e| BlockchainAccountIdVerifyError::HashError(e.to_string()))
}
// Dogecoin
#[cfg(feature = "ripemd160")]
["bip122", "1a91e3dace36e2be3bf030a65679fe82"] => {
crate::ripemd::hash_public_key(&jwk, 0x1e)
.map_err(|e| BlockchainAccountIdVerifyError::HashError(e.to_string()))
}
_ => Err(BlockchainAccountIdVerifyError::UnknownChainId(
self.chain_id.clone(),
)),
Expand Down
6 changes: 2 additions & 4 deletions src/ripemd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ use crate::jwk::{Params, JWK};

use ripemd160::{Digest, Ripemd160};

const VERSION_BYTE_BITCOIN_MAINNET: u8 = 0;

pub fn hash_public_key(jwk: &JWK) -> Result<String, Error> {
pub fn hash_public_key(jwk: &JWK, version: u8) -> Result<String, Error> {
let ec_params = match jwk.params {
Params::EC(ref params) => params,
_ => return Err(Error::UnsupportedKeyType),
Expand All @@ -21,7 +19,7 @@ pub fn hash_public_key(jwk: &JWK) -> Result<String, Error> {
let pk_sha256 = sha256(&pk_bytes)?;
let pk_ripemd160 = Ripemd160::digest(&pk_sha256);
let mut extended_ripemd160 = Vec::with_capacity(21);
extended_ripemd160.extend_from_slice(&[VERSION_BYTE_BITCOIN_MAINNET]);
extended_ripemd160.extend_from_slice(&[version]);
extended_ripemd160.extend_from_slice(&pk_ripemd160);
let addr = bs58::encode(&extended_ripemd160).with_check().into_string();
Ok(addr)
Expand Down

0 comments on commit 705a319

Please sign in to comment.