diff --git a/Cargo.toml b/Cargo.toml index a2fa79b..04a3616 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,9 @@ rustify = "0.5.3" rustify_derive = "0.5.2" eth_checksum = "0.1.2" base64 = "0.21.0" +hex = "0.4.3" [dev-dependencies] tokio = { version = "1.20.1", features = ["full"] } wiremock = "0.5.17" secp256k1 = "0.26.0" -hex = "0.4.3" diff --git a/examples/ethereum_wallet.rs b/examples/ethereum_wallet.rs index 7a9788b..c764a25 100644 --- a/examples/ethereum_wallet.rs +++ b/examples/ethereum_wallet.rs @@ -12,19 +12,19 @@ async fn main() { ) .unwrap(); - let created_account = quorum_vault_client::api::create_account(&client, "quorum") + let created_account = quorum_vault_client::api::ethereum::create_account(&client, "quorum") .await .unwrap(); println!("result: {:?}", created_account); - let addresses = quorum_vault_client::api::list_accounts(&client, "quorum") + let addresses = quorum_vault_client::api::ethereum::list_accounts(&client, "quorum") .await .unwrap(); println!("addresses: {:?}", addresses); let address = addresses.keys.first().copied().unwrap(); - let result = quorum_vault_client::api::read_account(&client, "quorum", address) + let result = quorum_vault_client::api::ethereum::read_account(&client, "quorum", address) .await .unwrap(); println!("result: {:?}", result); @@ -39,8 +39,15 @@ async fn main() { tx.gas_price = Some(U256::from(1)); - let signature = quorum_vault_client::api::sign_transaction(&client, "quorum", 1, tx) + let signature = quorum_vault_client::api::ethereum::sign_transaction(&client, "quorum", 1, tx) .await .unwrap(); println!("signature: {:?}", signature); + + let data = b"Hello, world!"; + let signature2 = quorum_vault_client::api::ethereum::sign(&client, "quorum", address, data) + .await + .unwrap(); + + println!("signature2: {:?}", signature2); } diff --git a/examples/keys.rs b/examples/keys.rs index bc0b632..7c0f93f 100644 --- a/examples/keys.rs +++ b/examples/keys.rs @@ -1,4 +1,4 @@ -use quorum_vault_client::api::KeyCryptoAlgorithm; +use quorum_vault_client::api::keys::KeyCryptoAlgorithm; use vaultrs::client::{VaultClient, VaultClientSettingsBuilder}; #[tokio::main] @@ -15,7 +15,7 @@ async fn main() { let key_id = "some-id"; - let new_key = quorum_vault_client::api::create_key( + let new_key = quorum_vault_client::api::keys::create_key( &client, "quorum", key_id, @@ -28,23 +28,23 @@ async fn main() { .unwrap(); println!("key: {new_key:?}"); - let keys = quorum_vault_client::api::list_keys(&client, "quorum") + let keys = quorum_vault_client::api::keys::list_keys(&client, "quorum") .await .unwrap(); println!("keys: {keys:?}"); - let read_key = quorum_vault_client::api::read_key(&client, "quorum", key_id) + let read_key = quorum_vault_client::api::keys::read_key(&client, "quorum", key_id) .await .unwrap(); println!("read_key: {read_key:?}"); let signature = - quorum_vault_client::api::sign(&client, "quorum", key_id, "some-data".as_bytes()) + quorum_vault_client::api::keys::sign(&client, "quorum", key_id, "some-data".as_bytes()) .await .unwrap(); println!("signature: {signature:?}"); - let updated_tags = quorum_vault_client::api::update_key_tags( + let updated_tags = quorum_vault_client::api::keys::update_key_tags( &client, "quorum", key_id, @@ -56,7 +56,7 @@ async fn main() { .unwrap(); println!("updated_tags: {updated_tags:?}"); - quorum_vault_client::api::destroy_key(&client, "quorum", "some-id") + quorum_vault_client::api::keys::destroy_key(&client, "quorum", "some-id") .await .unwrap(); println!("destroyed key: {key_id}"); diff --git a/examples/zksnarks.rs b/examples/zksnarks.rs index 4292ac6..5516d0d 100644 --- a/examples/zksnarks.rs +++ b/examples/zksnarks.rs @@ -13,26 +13,29 @@ async fn main() { .unwrap(); // Create a new account - let account = quorum_vault_client::api::create_zksnarks_account(&client, "quorum") + let account = quorum_vault_client::api::zksnarks::create_zksnarks_account(&client, "quorum") .await .unwrap(); println!("account: {:?}", account); // Read the account - let account = - quorum_vault_client::api::read_zksnarks_account(&client, "quorum", &account.public_key) - .await - .unwrap(); + let account = quorum_vault_client::api::zksnarks::read_zksnarks_account( + &client, + "quorum", + &account.public_key, + ) + .await + .unwrap(); println!("account: {:?}", account); // List the accounts - let accounts = quorum_vault_client::api::list_zksnarks_accounts(&client, "quorum") + let accounts = quorum_vault_client::api::zksnarks::list_zksnarks_accounts(&client, "quorum") .await .unwrap(); println!("accounts: {:?}", accounts); // Sign a message - let signature = quorum_vault_client::api::zksnarks_sign( + let signature = quorum_vault_client::api::zksnarks::zksnarks_sign( &client, "quorum", &account.public_key, diff --git a/src/api.rs b/src/api.rs index 4bb5d6a..798ea99 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,388 +1,3 @@ -use crate::api::ethereum::requests::{ - CreateEthereumAccountRequest, ImportPrivateKeyRequest, ListEthereumAccountsRequest, - ReadEthereumAccountRequest, SignEthereumTransactionRequest, -}; -use crate::api::ethereum::responses::{ - EthereumAccountResponse, EthereumAccountsResponse, EthereumSignTransactionResponse, -}; -use crate::api::keys::requests::{ - CreateKeyRequest, DestroyKeyRequest, ImportKeyRequest, ListKeysRequest, ReadKeyRequest, - SignRequest, UpdateKeyTagsRequest, -}; -use crate::api::keys::responses::{KeyResponse, KeysResponse, SignResponse}; -use crate::api::zksnarks::requests::{ - CreateZkSnarksAccountRequest, ListZkSnarksAccountsRequest, ReadZkSnarksAccountRequest, - ZkSnarksSignRequest, -}; -use crate::api::zksnarks::responses::{ - ZkSnarksAccountResponse, ZkSnarksAccountsResponse, ZkSnarksSignResponse, -}; -use crate::error::ClientError; -use crate::H256; -use base64::Engine; -use std::collections::HashMap; -use vaultrs::client::Client; -use web3::signing::keccak256; -use web3::types::{Address, TransactionRequest}; - pub mod ethereum; pub mod keys; pub mod zksnarks; - -/// Key crypto algorithm. -pub enum KeyCryptoAlgorithm { - Secp256k1, - Babyjubjub, -} - -/// Create a new Ethereum account. -/// -/// See [CreateEthereumAccountRequest] -pub async fn create_account( - client: &impl Client, - mount: &str, -) -> Result { - let request = CreateEthereumAccountRequest::builder() - .mount(mount) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// List Ethereum accounts. -/// -/// See [ListEthereumAccountsRequest] -pub async fn list_accounts( - client: &impl Client, - mount: &str, -) -> Result { - let request = ListEthereumAccountsRequest::builder() - .mount(mount) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// Read an Ethereum account. -/// -/// See [ReadEthereumAccountRequest] -pub async fn read_account( - client: &impl Client, - mount: &str, - address: Address, -) -> Result { - let address = format!("{address:?}"); - let checksummed = eth_checksum::checksum(&address); - let request = ReadEthereumAccountRequest::builder() - .mount(mount) - .address(checksummed) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// Sign an Ethereum transaction. -/// -/// See [SignEthereumTransactionRequest] -pub async fn sign_transaction( - client: &impl Client, - mount: &str, - chain_id: u64, - transaction: TransactionRequest, -) -> Result { - let address = format!("{:?}", transaction.from); - let checksummed = eth_checksum::checksum(&address); - let request = SignEthereumTransactionRequest::builder() - .mount(mount) - .address(checksummed) - .chain_id(chain_id.to_string()) - .amount(transaction.value.unwrap_or_default().to_string()) - .gas_limit(transaction.gas.map(|v| v.as_u64()).unwrap_or(21000)) - .gas_price(transaction.gas_price.unwrap_or_default().to_string()) - .nonce(transaction.nonce.unwrap_or_default().as_u64()) - .to(format!("{:?}", transaction.to.unwrap_or_default())) - .data(transaction.data.unwrap_or_default()) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// Import a Private Key -/// See [ImportPrivateKeyRequest] -pub async fn import_private_key( - client: &impl Client, - mount: &str, - private_key: &str, -) -> Result { - let request = ImportPrivateKeyRequest::builder() - .mount(mount) - .private_key(private_key) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// Create a new Key -/// See [CreateKeyRequest] -pub async fn create_key( - client: &impl Client, - mount: &str, - id: &str, - algorithm: KeyCryptoAlgorithm, - tags: HashMap, -) -> Result { - let request = CreateKeyRequest::builder() - .mount(mount) - .id(id) - .signing_algorithm(algorithm.signing_algorithm().to_string()) - .curve(algorithm.curve().to_string()) - .tags(tags) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// Read a Key -/// See [ReadKeyRequest] -pub async fn read_key( - client: &impl Client, - mount: &str, - id: &str, -) -> Result { - let request = ReadKeyRequest::builder() - .mount(mount) - .id(id) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// List Keys -/// See [ListKeysRequest] -pub async fn list_keys(client: &impl Client, mount: &str) -> Result { - let request = ListKeysRequest::builder().mount(mount).build().unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// Update a Key tags -/// See [UpdateKeyTagsRequest] -pub async fn update_key_tags( - client: &impl Client, - mount: &str, - id: &str, - tags: HashMap, -) -> Result { - let request = UpdateKeyTagsRequest::builder() - .mount(mount) - .id(id) - .tags(tags) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// Destroy a Key -/// See [DestroyKeyRequest] -pub async fn destroy_key(client: &impl Client, mount: &str, id: &str) -> Result<(), ClientError> { - let request = DestroyKeyRequest::builder() - .mount(mount) - .id(id) - .build() - .unwrap(); - vaultrs::api::exec_with_empty_result(client, request) - .await - .map_err(Into::into) -} - -/// Import a Key -/// See [ImportKeyRequest] -pub async fn import_key( - client: &impl Client, - mount: &str, - id: &str, - algorithm: KeyCryptoAlgorithm, - tags: HashMap, - private_key: &str, -) -> Result { - let request = ImportKeyRequest::builder() - .mount(mount) - .id(id) - .signing_algorithm(algorithm.signing_algorithm().to_string()) - .curve(algorithm.curve().to_string()) - .tags(tags) - .private_key(private_key) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// Sign a message -/// See [SignRequest] -pub async fn sign( - client: &impl Client, - mount: &str, - id: &str, - data: &[u8], -) -> Result { - let hash = keccak256(data); - let encoded = base64::prelude::BASE64_URL_SAFE.encode(hash); - let request = SignRequest::builder() - .mount(mount) - .id(id) - .data(encoded) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// Sign a message -/// Data must be a 32 byte hash -/// See [SignRequest] -pub async fn sign_hash( - client: &impl Client, - mount: &str, - id: &str, - data: [u8; 32], -) -> Result { - let encoded = base64::prelude::BASE64_URL_SAFE.encode(data); - let request = SignRequest::builder() - .mount(mount) - .id(id) - .data(encoded) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -impl KeyCryptoAlgorithm { - /// Returns the curve name for the algorithm - pub fn curve(&self) -> &'static str { - match self { - KeyCryptoAlgorithm::Secp256k1 => "secp256k1", - KeyCryptoAlgorithm::Babyjubjub => "babyjubjub", - } - } - - /// Returns the signing algorithm name for the algorithm - pub fn signing_algorithm(&self) -> &'static str { - match self { - KeyCryptoAlgorithm::Secp256k1 => "ecdsa", - KeyCryptoAlgorithm::Babyjubjub => "eddsa", - } - } -} - -/// Create a zk-SNARKs account (eddsa) -/// See [CreateZkSnarksAccountRequest] -pub async fn create_zksnarks_account( - client: &impl Client, - mount: &str, -) -> Result { - let request = CreateZkSnarksAccountRequest::builder() - .mount(mount) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// Read a zk-SNARKs account -/// See [ReadZkSnarksAccountRequest] -pub async fn read_zksnarks_account( - client: &impl Client, - mount: &str, - id: &str, -) -> Result { - let request = ReadZkSnarksAccountRequest::builder() - .mount(mount) - .id(id) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// List zk-SNARKs accounts -/// See [ListZkSnarksAccountsRequest] -pub async fn list_zksnarks_accounts( - client: &impl Client, - mount: &str, -) -> Result { - let request = ListZkSnarksAccountsRequest::builder() - .mount(mount) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// Sign a message with a zk-SNARKs account (eddsa) -/// See [ZkSnarksSignResponse] -pub async fn zksnarks_sign( - client: &impl Client, - mount: &str, - id: &str, - data: &[u8], -) -> Result { - let hash = keccak256(data); - let hex = H256::from(hash); - let encoded = format!("{:?}", hex); - let request = ZkSnarksSignRequest::builder() - .mount(mount) - .id(id) - .data(encoded) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} - -/// Sign a message with a zk-SNARKs account (eddsa) -/// Data must be a 32 byte hash -/// See [ZkSnarksSignResponse] -pub async fn zksnarks_sign_hash( - client: &impl Client, - mount: &str, - id: &str, - data: [u8; 32], -) -> Result { - let hex = H256::from(data); - let encoded = format!("{:?}", hex); - let request = ZkSnarksSignRequest::builder() - .mount(mount) - .id(id) - .data(encoded) - .build() - .unwrap(); - vaultrs::api::exec_with_result(client, request) - .await - .map_err(Into::into) -} diff --git a/src/api/ethereum.rs b/src/api/ethereum.rs index 116da0f..361b5b0 100644 --- a/src/api/ethereum.rs +++ b/src/api/ethereum.rs @@ -1,2 +1,135 @@ +use crate::api::ethereum::requests::{ + CreateEthereumAccountRequest, ImportPrivateKeyRequest, ListEthereumAccountsRequest, + ReadEthereumAccountRequest, SignEthereumTransactionRequest, +}; +use crate::api::ethereum::responses::{ + EthereumAccountResponse, EthereumAccountsResponse, EthereumSignTransactionResponse, +}; + +use vaultrs::client::Client; +use vaultrs::error::ClientError; +use web3::types::{Address, TransactionRequest}; + +use self::requests::EthereumSignRequest; +use self::responses::EthereumSignResponse; + pub mod requests; pub mod responses; + +/// Create a new Ethereum account. +/// +/// See [CreateEthereumAccountRequest] +pub async fn create_account( + client: &impl Client, + mount: &str, +) -> Result { + let request = CreateEthereumAccountRequest::builder() + .mount(mount) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// List Ethereum accounts. +/// +/// See [ListEthereumAccountsRequest] +pub async fn list_accounts( + client: &impl Client, + mount: &str, +) -> Result { + let request = ListEthereumAccountsRequest::builder() + .mount(mount) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// Read an Ethereum account. +/// +/// See [ReadEthereumAccountRequest] +pub async fn read_account( + client: &impl Client, + mount: &str, + address: Address, +) -> Result { + let address = format!("{address:?}"); + let checksummed = eth_checksum::checksum(&address); + let request = ReadEthereumAccountRequest::builder() + .mount(mount) + .address(checksummed) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// Sign an Ethereum transaction. +/// +/// See [SignEthereumTransactionRequest] +pub async fn sign_transaction( + client: &impl Client, + mount: &str, + chain_id: u64, + transaction: TransactionRequest, +) -> Result { + let address = format!("{:?}", transaction.from); + let checksummed = eth_checksum::checksum(&address); + let request = SignEthereumTransactionRequest::builder() + .mount(mount) + .address(checksummed) + .chain_id(chain_id.to_string()) + .amount(transaction.value.unwrap_or_default().to_string()) + .gas_limit(transaction.gas.map(|v| v.as_u64()).unwrap_or(21000)) + .gas_price(transaction.gas_price.unwrap_or_default().to_string()) + .nonce(transaction.nonce.unwrap_or_default().as_u64()) + .to(format!("{:?}", transaction.to.unwrap_or_default())) + .data(transaction.data.unwrap_or_default()) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// Import a Private Key +/// See [ImportPrivateKeyRequest] +pub async fn import_private_key( + client: &impl Client, + mount: &str, + private_key: &str, +) -> Result { + let request = ImportPrivateKeyRequest::builder() + .mount(mount) + .private_key(private_key) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// Sign a message with an Ethereum account. +/// See [SignEthereumRequest] +pub async fn sign( + client: &impl Client, + mount: &str, + address: Address, + data: &[u8], +) -> Result { + let address = format!("{address:?}"); + let checksummed = eth_checksum::checksum(&address); + let request = EthereumSignRequest::builder() + .mount(mount) + .address(checksummed) + .data(format!("0x{}", hex::encode(data))) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} diff --git a/src/api/ethereum/requests.rs b/src/api/ethereum/requests.rs index be75940..688b946 100644 --- a/src/api/ethereum/requests.rs +++ b/src/api/ethereum/requests.rs @@ -1,5 +1,6 @@ use crate::api::ethereum::responses::{ - EthereumAccountResponse, EthereumAccountsResponse, EthereumSignTransactionResponse, + EthereumAccountResponse, EthereumAccountsResponse, EthereumSignResponse, + EthereumSignTransactionResponse, }; use rustify_derive::Endpoint; use web3::types::Bytes; @@ -119,3 +120,26 @@ pub struct ImportPrivateKeyRequest { #[endpoint(body)] pub private_key: String, } + +/// ## Sign Arbitrary Message +/// This endpoint signs an arbitrary message. +/// +/// * Path: {self.mount}/ethereum/accounts/{self.address}/sign +/// * Method: POST +/// * Response: [EthereumSignResponse] +#[derive(Builder, Debug, Endpoint)] +#[endpoint( + path = "{self.mount}/ethereum/accounts/{self.address}/sign", + method = "POST", + response = "EthereumSignResponse", + builder = "true" +)] +#[builder(setter(into))] +pub struct EthereumSignRequest { + #[endpoint(skip)] + pub mount: String, + #[endpoint(skip)] + pub address: String, + #[endpoint(body)] + pub data: String, +} diff --git a/src/api/ethereum/responses.rs b/src/api/ethereum/responses.rs index 6a79af9..2cb11cb 100644 --- a/src/api/ethereum/responses.rs +++ b/src/api/ethereum/responses.rs @@ -24,3 +24,10 @@ pub struct EthereumSignTransactionResponse { pub struct EthereumAccountsResponse { pub keys: Vec
, } + +/// Response from executing +/// [EthereumSignRequest][crate::api::ethereum::requests::EthereumSignRequest] +#[derive(Deserialize, Debug, Serialize)] +pub struct EthereumSignResponse { + pub signature: String, +} diff --git a/src/api/keys.rs b/src/api/keys.rs index 116da0f..ddab631 100644 --- a/src/api/keys.rs +++ b/src/api/keys.rs @@ -1,2 +1,184 @@ +use std::collections::HashMap; + +use base64::Engine; +use vaultrs::client::Client; +use vaultrs::error::ClientError; + +use crate::api::keys::requests::{ + CreateKeyRequest, DestroyKeyRequest, ImportKeyRequest, ListKeysRequest, ReadKeyRequest, + SignRequest, UpdateKeyTagsRequest, +}; +use crate::api::keys::responses::{KeyResponse, KeysResponse, SignResponse}; + pub mod requests; pub mod responses; + +/// Key crypto algorithm. +pub enum KeyCryptoAlgorithm { + Secp256k1, + Babyjubjub, +} + +impl KeyCryptoAlgorithm { + /// Returns the curve name for the algorithm + pub fn curve(&self) -> &'static str { + match self { + KeyCryptoAlgorithm::Secp256k1 => "secp256k1", + KeyCryptoAlgorithm::Babyjubjub => "babyjubjub", + } + } + + /// Returns the signing algorithm name for the algorithm + pub fn signing_algorithm(&self) -> &'static str { + match self { + KeyCryptoAlgorithm::Secp256k1 => "ecdsa", + KeyCryptoAlgorithm::Babyjubjub => "eddsa", + } + } +} + +/// Create a new Key +/// See [CreateKeyRequest] +pub async fn create_key( + client: &impl Client, + mount: &str, + id: &str, + algorithm: KeyCryptoAlgorithm, + tags: HashMap, +) -> Result { + let request = CreateKeyRequest::builder() + .mount(mount) + .id(id) + .signing_algorithm(algorithm.signing_algorithm().to_string()) + .curve(algorithm.curve().to_string()) + .tags(tags) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// Read a Key +/// See [ReadKeyRequest] +pub async fn read_key( + client: &impl Client, + mount: &str, + id: &str, +) -> Result { + let request = ReadKeyRequest::builder() + .mount(mount) + .id(id) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// List Keys +/// See [ListKeysRequest] +pub async fn list_keys(client: &impl Client, mount: &str) -> Result { + let request = ListKeysRequest::builder().mount(mount).build().unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// Update a Key tags +/// See [UpdateKeyTagsRequest] +pub async fn update_key_tags( + client: &impl Client, + mount: &str, + id: &str, + tags: HashMap, +) -> Result { + let request = UpdateKeyTagsRequest::builder() + .mount(mount) + .id(id) + .tags(tags) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// Destroy a Key +/// See [DestroyKeyRequest] +pub async fn destroy_key(client: &impl Client, mount: &str, id: &str) -> Result<(), ClientError> { + let request = DestroyKeyRequest::builder() + .mount(mount) + .id(id) + .build() + .unwrap(); + vaultrs::api::exec_with_empty_result(client, request) + .await + .map_err(Into::into) +} + +/// Import a Key +/// See [ImportKeyRequest] +pub async fn import_key( + client: &impl Client, + mount: &str, + id: &str, + algorithm: KeyCryptoAlgorithm, + tags: HashMap, + private_key: &str, +) -> Result { + let request = ImportKeyRequest::builder() + .mount(mount) + .id(id) + .signing_algorithm(algorithm.signing_algorithm().to_string()) + .curve(algorithm.curve().to_string()) + .tags(tags) + .private_key(private_key) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// Sign a message +/// See [SignRequest] +pub async fn sign( + client: &impl Client, + mount: &str, + id: &str, + data: &[u8], +) -> Result { + let hash = web3::signing::keccak256(data); + let encoded = base64::prelude::BASE64_URL_SAFE.encode(hash); + let request = SignRequest::builder() + .mount(mount) + .id(id) + .data(encoded) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// Sign a message +/// Data must be a 32 byte hash +/// See [SignRequest] +pub async fn sign_hash( + client: &impl Client, + mount: &str, + id: &str, + data: [u8; 32], +) -> Result { + let encoded = base64::prelude::BASE64_URL_SAFE.encode(data); + let request = SignRequest::builder() + .mount(mount) + .id(id) + .data(encoded) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} diff --git a/src/api/zksnarks.rs b/src/api/zksnarks.rs index 116da0f..7d74fb3 100644 --- a/src/api/zksnarks.rs +++ b/src/api/zksnarks.rs @@ -1,2 +1,104 @@ +use crate::api::zksnarks::requests::{ + CreateZkSnarksAccountRequest, ListZkSnarksAccountsRequest, ReadZkSnarksAccountRequest, + ZkSnarksSignRequest, +}; +use crate::api::zksnarks::responses::{ + ZkSnarksAccountResponse, ZkSnarksAccountsResponse, ZkSnarksSignResponse, +}; +use crate::error::ClientError; +use crate::H256; +use vaultrs::client::Client; + pub mod requests; pub mod responses; + +/// Create a zk-SNARKs account (eddsa) +/// See [CreateZkSnarksAccountRequest] +pub async fn create_zksnarks_account( + client: &impl Client, + mount: &str, +) -> Result { + let request = CreateZkSnarksAccountRequest::builder() + .mount(mount) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// Read a zk-SNARKs account +/// See [ReadZkSnarksAccountRequest] +pub async fn read_zksnarks_account( + client: &impl Client, + mount: &str, + id: &str, +) -> Result { + let request = ReadZkSnarksAccountRequest::builder() + .mount(mount) + .id(id) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// List zk-SNARKs accounts +/// See [ListZkSnarksAccountsRequest] +pub async fn list_zksnarks_accounts( + client: &impl Client, + mount: &str, +) -> Result { + let request = ListZkSnarksAccountsRequest::builder() + .mount(mount) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// Sign a message with a zk-SNARKs account (eddsa) +/// See [ZkSnarksSignResponse] +pub async fn zksnarks_sign( + client: &impl Client, + mount: &str, + id: &str, + data: &[u8], +) -> Result { + let hash = web3::signing::keccak256(data); + let hex = H256::from(hash); + let encoded = format!("{:?}", hex); + let request = ZkSnarksSignRequest::builder() + .mount(mount) + .id(id) + .data(encoded) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} + +/// Sign a message with a zk-SNARKs account (eddsa) +/// Data must be a 32 byte hash +/// See [ZkSnarksSignResponse] +pub async fn zksnarks_sign_hash( + client: &impl Client, + mount: &str, + id: &str, + data: [u8; 32], +) -> Result { + let hex = H256::from(data); + let encoded = format!("{:?}", hex); + let request = ZkSnarksSignRequest::builder() + .mount(mount) + .id(id) + .data(encoded) + .build() + .unwrap(); + vaultrs::api::exec_with_result(client, request) + .await + .map_err(Into::into) +} diff --git a/src/lib.rs b/src/lib.rs index 4bc519b..788a1e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,7 +70,7 @@ //! ).unwrap(); //! //! // By default the plugin mounts the Ethereum backend at the path "quorum" -//! let created_account = quorum_vault_client::api::create_account(&client, "quorum").await.unwrap(); +//! let created_account = quorum_vault_client::api::ethereum::create_account(&client, "quorum").await.unwrap(); //! println!("result: {:?}", created_account); //! } //! @@ -99,7 +99,7 @@ //! .unwrap() //! ).unwrap(); //! -//! let list_accounts = quorum_vault_client::api::list_accounts(&client, "quorum").await.unwrap(); +//! let list_accounts = quorum_vault_client::api::ethereum::list_accounts(&client, "quorum").await.unwrap(); //! println!("result: {:?}", list_accounts); //! } //! @@ -131,7 +131,7 @@ //! ).unwrap(); //! //! let address = Address::from_str("0x8d3113e29CB92F44F1762E52D2a0276509b36b82").unwrap(); -//! let read_account = quorum_vault_client::api::read_account(&client, "quorum", address).await.unwrap(); +//! let read_account = quorum_vault_client::api::ethereum::read_account(&client, "quorum", address).await.unwrap(); //! println!("result: {:?}", read_account); //! } //! @@ -173,7 +173,7 @@ //! //! tx.gas_price = Some(U256::from(1)); //! -//! let sign_transaction = quorum_vault_client::api::sign_transaction(&client, "quorum", 1, tx).await.unwrap(); +//! let sign_transaction = quorum_vault_client::api::ethereum::sign_transaction(&client, "quorum", 1, tx).await.unwrap(); //! println!("result: {:?}", sign_transaction); //! } //! @@ -193,7 +193,7 @@ //! //! ```no_run //! use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder}; -//! use quorum_vault_client::api::KeyCryptoAlgorithm; +//! use quorum_vault_client::api::keys::KeyCryptoAlgorithm; //! //! #[tokio::main] //! async fn main() { @@ -206,7 +206,7 @@ //! .unwrap() //! ).unwrap(); //! -//! let created_key = quorum_vault_client::api::create_key(&client, "quorum", "some-id", KeyCryptoAlgorithm::Secp256k1, [("tag".to_string(), "value".to_string())].into_iter().collect()).await.unwrap(); +//! let created_key = quorum_vault_client::api::keys::create_key(&client, "quorum", "some-id", KeyCryptoAlgorithm::Secp256k1, [("tag".to_string(), "value".to_string())].into_iter().collect()).await.unwrap(); //! //! println!("result: {:?}", created_key); //! } @@ -236,7 +236,7 @@ //! .unwrap() //! ).unwrap(); //! -//! let key = quorum_vault_client::api::read_key(&client, "quorum", "some-id").await.unwrap(); +//! let key = quorum_vault_client::api::keys::read_key(&client, "quorum", "some-id").await.unwrap(); //! println!("result: {:?}", key); //! } //! ``` @@ -265,7 +265,7 @@ //! .unwrap() //! ).unwrap(); //! -//! let keys = quorum_vault_client::api::list_keys(&client, "quorum").await.unwrap(); +//! let keys = quorum_vault_client::api::keys::list_keys(&client, "quorum").await.unwrap(); //! println!("result: {:?}", keys); //! } //! ``` @@ -294,7 +294,7 @@ //! .unwrap() //! ).unwrap(); //! -//! quorum_vault_client::api::destroy_key(&client, "quorum", "some-id").await.unwrap(); +//! quorum_vault_client::api::keys::destroy_key(&client, "quorum", "some-id").await.unwrap(); //! } //! ``` //! @@ -316,7 +316,7 @@ //! .unwrap() //! ).unwrap(); //! -//! let signature = quorum_vault_client::api::sign(&client, "quorum", "some-id", "some-data".as_bytes()).await.unwrap(); +//! let signature = quorum_vault_client::api::keys::sign(&client, "quorum", "some-id", "some-data".as_bytes()).await.unwrap(); //! println!("signature: {:?}", signature); //! } //! ``` diff --git a/tests/api/ethereum.rs b/tests/api/ethereum.rs index 7692c17..0c26559 100644 --- a/tests/api/ethereum.rs +++ b/tests/api/ethereum.rs @@ -39,7 +39,9 @@ async fn test_create_wallet() { .mount(&mock) .await; - let wallet = api::create_account(&vault_client, "quorum").await.unwrap(); + let wallet = api::ethereum::create_account(&vault_client, "quorum") + .await + .unwrap(); assert_eq!( format!("{:?}", wallet.address), @@ -87,7 +89,9 @@ async fn test_get_list_accounts() { .mount(&mock) .await; - let accounts = api::list_accounts(&vault_client, "quorum").await.unwrap(); + let accounts = api::ethereum::list_accounts(&vault_client, "quorum") + .await + .unwrap(); assert_eq!(accounts.keys.len(), 2); assert_eq!( @@ -138,7 +142,7 @@ async fn test_read_account() { .mount(&mock) .await; - let account = api::read_account(&vault_client, "quorum", address) + let account = api::ethereum::read_account(&vault_client, "quorum", address) .await .unwrap(); @@ -208,7 +212,7 @@ async fn test_sign_transaction() { tx.gas_price = Some(U256::from(1)); - let signature = api::sign_transaction(&vault_client, "quorum", 1, tx) + let signature = api::ethereum::sign_transaction(&vault_client, "quorum", 1, tx) .await .unwrap(); @@ -253,7 +257,7 @@ async fn test_import_private_key() { .mount(&mock) .await; - let wallet = api::import_private_key( + let wallet = api::ethereum::import_private_key( &vault_client, "quorum", "0a1232595b77534d99364bfde13383accbcb40775967a7eacd15d355c96288a5", @@ -272,3 +276,52 @@ async fn test_import_private_key() { assert_eq!(wallet.public_key, "0x046b5ae5ec570abb9c4c50746d08fb63c911641170581b07f5f531a993b8b6cbeced5f8e3de3f4c7416a7661ed2c7eef8fea416c62df47ec43896af26086b87594"); assert_eq!(wallet.namespace, ""); } + +#[tokio::test] +async fn test_sign_message() { + let mock = MockServer::start().await; + let vault_client = VaultClient::new( + VaultClientSettingsBuilder::default() + .address(mock.uri()) + .token("s.1234567890abcdef") + .build() + .unwrap(), + ) + .unwrap(); + + let data = b"Hello, world!"; + + let expected_request = serde_json::json!({ + "data": "0x48656c6c6f2c20776f726c6421" + }); + + let response = serde_json::json!({ + "request_id": "e81af2c4-4e4c-a640-0f8f-99ce3f7d486a", + "lease_id": "", + "renewable": false, + "lease_duration": 0, + "data": { + "signature": "0xe7905251968e28d6a3696e0c01e5b20ce9e83f185848fe91804d74d958b2aadd28846e605f4e7efac7b4446508607b35e46151f72a6e917e82241781206418d601" + }, + "wrap_info": null, + "warnings": null, + "auth": null + }); + + Mock::given(method("POST")) + .and(path( + "/v1/quorum/ethereum/accounts/0xAd38E61dB0D3f8fEF9B4c5DD0C1A9F691cdCcfF5/sign", + )) + .and(body_json(&expected_request)) + .respond_with(ResponseTemplate::new(200).set_body_json(&response)) + .mount(&mock) + .await; + + let address = Address::from_str("0xAd38E61dB0D3f8fEF9B4c5DD0C1A9F691cdCcfF5").unwrap(); + + let signature = api::ethereum::sign(&vault_client, "quorum", address, data) + .await + .unwrap(); + + assert_eq!(signature.signature, "0xe7905251968e28d6a3696e0c01e5b20ce9e83f185848fe91804d74d958b2aadd28846e605f4e7efac7b4446508607b35e46151f72a6e917e82241781206418d601"); +} diff --git a/tests/api/keys.rs b/tests/api/keys.rs index 191a1bb..1edf47d 100644 --- a/tests/api/keys.rs +++ b/tests/api/keys.rs @@ -1,5 +1,5 @@ use quorum_vault_client::api; -use quorum_vault_client::api::KeyCryptoAlgorithm; +use quorum_vault_client::api::keys::KeyCryptoAlgorithm; use vaultrs::client::{VaultClient, VaultClientSettingsBuilder}; use web3::signing::keccak256; use wiremock::matchers::{body_json, method, path}; @@ -58,7 +58,7 @@ async fn test_create_key() { .mount(&mock) .await; - let key = api::create_key( + let key = api::keys::create_key( &vault_client, "quorum", "dd4b594d-4b89-480d-a8a8-01ed7e1f0140", @@ -130,7 +130,7 @@ async fn test_read_key() { .mount(&mock) .await; - let key = api::read_key( + let key = api::keys::read_key( &vault_client, "quorum", "dd4b594d-4b89-480d-a8a8-01ed7e1f0140", @@ -186,7 +186,7 @@ async fn test_get_list_keys() { .mount(&mock) .await; - let keys = api::list_keys(&vault_client, "quorum").await.unwrap(); + let keys = api::keys::list_keys(&vault_client, "quorum").await.unwrap(); assert_eq!(keys.keys, vec!["dd4b594d-4b89-480d-a8a8-01ed7e1f0140"]); } @@ -230,7 +230,7 @@ async fn test_sign_data() { .mount(&mock) .await; - let signature = api::sign( + let signature = api::keys::sign( &vault_client, "quorum", "dd4b594d-4b89-480d-a8a8-01ed7e1f0140", @@ -285,7 +285,7 @@ async fn test_sign_hash() { let hash = keccak256(data.as_bytes()); - let signature = api::sign_hash( + let signature = api::keys::sign_hash( &vault_client, "quorum", "dd4b594d-4b89-480d-a8a8-01ed7e1f0140", @@ -349,7 +349,7 @@ async fn test_update_key_tags() { .mount(&mock) .await; - let key = api::update_key_tags( + let key = api::keys::update_key_tags( &vault_client, "quorum", "dd4b594d-4b89-480d-a8a8-01ed7e1f0140", @@ -398,7 +398,7 @@ async fn test_destroy_key() { .mount(&mock) .await; - api::destroy_key( + api::keys::destroy_key( &vault_client, "quorum", "dd4b594d-4b89-480d-a8a8-01ed7e1f0140", @@ -460,7 +460,7 @@ async fn test_import_key() { .mount(&mock) .await; - let key = api::import_key( + let key = api::keys::import_key( &vault_client, "quorum", "dd4b594d-4b89-480d-a8a8-01ed7e1f0140", diff --git a/tests/api/zksnarks.rs b/tests/api/zksnarks.rs index 8d29ff6..f05b164 100644 --- a/tests/api/zksnarks.rs +++ b/tests/api/zksnarks.rs @@ -38,7 +38,7 @@ async fn test_create_zksnarks_account() { .mount(&mock) .await; - let account = api::create_zksnarks_account(&vault_client, "quorum") + let account = api::zksnarks::create_zksnarks_account(&vault_client, "quorum") .await .unwrap(); @@ -84,7 +84,7 @@ async fn test_list_zksnarks_account() { .mount(&mock) .await; - let accounts = api::list_zksnarks_accounts(&vault_client, "quorum") + let accounts = api::zksnarks::list_zksnarks_accounts(&vault_client, "quorum") .await .unwrap(); @@ -129,7 +129,7 @@ async fn test_read_zksnarks_account() { .mount(&mock) .await; - let account = api::read_zksnarks_account( + let account = api::zksnarks::read_zksnarks_account( &vault_client, "quorum", "0x7e8249b895434a1b02aade22033b887620ab5e756aa106d415ff33ace9048626", @@ -184,7 +184,7 @@ async fn test_sign_message() { .mount(&mock) .await; - let signature = api::zksnarks_sign( + let signature = api::zksnarks::zksnarks_sign( &vault_client, "quorum", "0x7e8249b895434a1b02aade22033b887620ab5e756aa106d415ff33ace9048626", @@ -235,7 +235,7 @@ async fn test_sign_hash() { .mount(&mock) .await; - let signature = api::zksnarks_sign_hash( + let signature = api::zksnarks::zksnarks_sign_hash( &vault_client, "quorum", "0x7e8249b895434a1b02aade22033b887620ab5e756aa106d415ff33ace9048626",