use bluefin_api::models::AccountAuthorizationRequest;
use sui_sdk_types::SignatureScheme;
use crate::{
core::PrivateKey,
signature::{self, RequestExt, Result},
};
pub struct AccountAuthorizationRequestExt {
pub request: AccountAuthorizationRequest,
pub is_authorize: bool,
}
impl AccountAuthorizationRequestExt {
#[must_use]
pub fn new(request: AccountAuthorizationRequest, is_authorize: bool) -> Self {
Self {
request,
is_authorize,
}
}
}
impl RequestExt for AccountAuthorizationRequestExt {
fn sign(self, private_key: PrivateKey, scheme: SignatureScheme) -> Result<Self> {
let signature = if self.is_authorize {
signature::signature(
signature::conversion::signable::AuthorizeAccountRequest::from(
self.request.clone(),
),
private_key,
scheme,
)?
} else {
signature::signature(
signature::conversion::signable::DeauthorizeAccountRequest::from(
self.request.clone(),
),
private_key,
scheme,
)?
};
Ok(AccountAuthorizationRequestExt {
request: AccountAuthorizationRequest {
signature,
..self.request
},
is_authorize: self.is_authorize,
})
}
fn compute_hash(self) -> Result<String> {
if self.is_authorize {
let converted = signature::conversion::hashable::AuthorizeAccountRequest::try_from(
self.request.clone(),
)?;
signature::compute_hash(&converted)
} else {
let converted = signature::conversion::hashable::DeauthorizeAccountRequest::try_from(
self.request.clone(),
)?;
signature::compute_hash(&converted)
}
}
}
#[cfg(test)]
mod tests {
use bluefin_api::models::AccountAuthorizationRequestSignedFields;
use rand::rngs::OsRng;
use sui_crypto::{ed25519::Ed25519VerifyingKey, secp256k1::Secp256k1VerifyingKey};
use sui_sdk_types::{Ed25519PublicKey, Secp256k1PublicKey};
use crate::signature::testing::verify_signature;
use super::*;
fn verify_request_signature(request: &AccountAuthorizationRequestExt, signer_address: &str) {
assert!(!request.request.signature.is_empty());
let result = if request.is_authorize {
verify_signature(
signer_address,
&request.request.signature.clone(),
request.request.clone(),
signature::conversion::signable::AuthorizeAccountRequest::from,
)
} else {
verify_signature(
signer_address,
&request.request.signature.clone(),
request.request.clone(),
signature::conversion::signable::DeauthorizeAccountRequest::from,
)
};
match result {
Ok(()) => {}
Err(e) => panic!("{e}"),
}
}
#[test]
fn sign_authorize_request_is_successful_ed25519() {
let private_key = ed25519_dalek::SigningKey::generate(&mut OsRng);
let signer_address = Ed25519VerifyingKey::new(&Ed25519PublicKey::new(
private_key.verifying_key().to_bytes(),
))
.unwrap()
.public_key()
.derive_address()
.to_hex();
let request = account_authorization_request(true)
.sign(private_key.to_bytes(), SignatureScheme::Ed25519)
.unwrap();
verify_request_signature(&request, &signer_address);
}
#[test]
fn sign_authorize_request_is_successful_secp256k1() {
let private_key = secp256k1::SecretKey::new(&mut OsRng);
let secp = secp256k1::Secp256k1::new();
let signer_address = Secp256k1VerifyingKey::new(&Secp256k1PublicKey::new(
private_key.public_key(&secp).serialize(),
))
.unwrap()
.public_key()
.derive_address()
.to_hex();
let request = account_authorization_request(true)
.sign(private_key.secret_bytes(), SignatureScheme::Secp256k1)
.unwrap();
verify_request_signature(&request, &signer_address);
}
#[test]
fn sign_deauthorize_request_is_successful_ed25519() {
let private_key = ed25519_dalek::SigningKey::generate(&mut OsRng);
let signer_address = Ed25519VerifyingKey::new(&Ed25519PublicKey::new(
private_key.verifying_key().to_bytes(),
))
.unwrap()
.public_key()
.derive_address()
.to_hex();
let request = account_authorization_request(false)
.sign(private_key.to_bytes(), SignatureScheme::Ed25519)
.unwrap();
verify_request_signature(&request, &signer_address);
}
#[test]
fn sign_deauthorize_request_is_successful_secp256k1() {
let private_key = secp256k1::SecretKey::new(&mut OsRng);
let secp = secp256k1::Secp256k1::new();
let signer_address = Secp256k1VerifyingKey::new(&Secp256k1PublicKey::new(
private_key.public_key(&secp).serialize(),
))
.unwrap()
.public_key()
.derive_address()
.to_hex();
let request = account_authorization_request(false)
.sign(private_key.secret_bytes(), SignatureScheme::Secp256k1)
.unwrap();
verify_request_signature(&request, &signer_address);
}
#[test]
fn compute_hash_is_successful_authorize() {
let request = account_authorization_request(true);
let hash = request.compute_hash().unwrap();
assert!(!hash.is_empty());
}
#[test]
fn compute_hash_is_successful_deauthorize() {
let request = account_authorization_request(false);
let hash = request.compute_hash().unwrap();
assert!(!hash.is_empty());
}
fn account_authorization_request(is_authorize: bool) -> AccountAuthorizationRequestExt {
AccountAuthorizationRequestExt::new(
AccountAuthorizationRequest {
signed_fields: AccountAuthorizationRequestSignedFields {
account_address:
"0x8e78225d72b1d7b1f63e5e9f88f09b12ca66c84e2fc8b91fc10f6a0c51230615".into(),
authorized_account_address:
"0x8e78225d72b1d7b1f63e5e9f88f09b12ca66c84e2fc8b91fc10f6a0c51230615".into(),
ids_id: "0x8e78225d72b1d7b1f63e5e9f88f09b12ca66c84e2fc8b91fc10f6a0c51230615"
.into(),
salt: "1725930601205".into(),
signed_at_millis: 1_725_931_543_867,
},
..AccountAuthorizationRequest::default()
},
is_authorize,
)
}
}