A Rust client for the Quorum Vault Plugin API.
This client based on the Vault Client.
The following backends are supported:
- Ethereum
- Create Ethereum Account
- List Ethereum Accounts
- Read Ethereum Account by Address
- Sign Ethereum Transaction (only Legacy)
- Import Private Key
- Sign Arbitrary Data
- Keys
- Create Key
- List Keys
- Read Key
- Delete Key
- Sign Data
- Import Private Key
- Update Key Tags
- ZK-SNARKs
- Create ZK-SNARKs Account
- Read ZK-SNARKs Account
- List ZK-SNARKs Accounts
- Sign Data
Version 2.0 is a major release that replaces the unmaintained web3 crate with the actively maintained alloy ecosystem, updates the Rust edition to 2024, and introduces stricter typing across the API.
| Area | v1.x | v2.0 |
|---|---|---|
| Rust edition | 2021 | 2024 |
| Ethereum types | web3 = "0.19" |
alloy-primitives = "1" |
| Transaction types | web3::types::TransactionRequest |
alloy-rpc-types-eth::TransactionRequest |
| Address checksumming | eth_checksum crate |
Built-in Address::to_checksum(None) |
| Keccak256 hashing | web3::signing::keccak256() |
alloy_primitives::keccak256() |
| Re-exports | pub use web3::types::* |
pub use alloy_primitives::* |
Remove web3 from your dependencies and add alloy-primitives if you use Ethereum primitive types directly:
[dependencies]
quorum-vault-client = "2.0.0"
# Only needed if you construct alloy types directly:
# alloy-primitives = "1"
# alloy-rpc-types-eth = "1"The crate now re-exports types from alloy_primitives instead of web3::types:
// v1.x
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
use web3::types::{Address, U256, TransactionRequest};
// v2.0
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder, Address, U256, TransactionRequest};All commonly used types (Address, U256, Bytes, etc.) keep the same names but come from alloy_primitives. If you were importing web3::types::* directly, replace those imports with alloy_primitives::*.
Note:
H256from web3 is replaced byB256in alloy.
The TransactionRequest builder API has changed significantly:
// v1.x
let mut tx = TransactionRequest::builder()
.from(address)
.to(address)
.value(U256::from_dec_str("1000000000000000000").unwrap())
.gas(U256::from(21000))
.nonce(U256::from(0))
.build();
tx.gas_price = Some(U256::from(1));
// v2.0
let tx = TransactionRequest::default()
.from(address)
.to(address)
.value(U256::from_str("1000000000000000000").unwrap())
.gas_limit(21000)
.gas_price(1)
.nonce(0);Key differences:
TransactionRequest::builder().build()→TransactionRequest::default()(no.build()call).gas(U256)→.gas_limit(u64)— accepts a native integer.nonce(U256)→.nonce(u64)— accepts a native integer.gas_priceis now set via the builder chain, not as a mutable fieldU256::from_dec_str(...)→U256::from_str(...)(requiresuse std::str::FromStr)
The import_private_key function now accepts B256 instead of &str for type safety:
// v1.x
quorum_vault_client::api::ethereum::import_private_key(
&client, "quorum", "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
).await.unwrap();
// v2.0
use quorum_vault_client::B256;
let private_key: B256 = "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
.parse()
.unwrap();
quorum_vault_client::api::ethereum::import_private_key(
&client, "quorum", private_key
).await.unwrap();API functions are now organized under explicit submodules. Update all call sites:
// Ethereum
quorum_vault_client::api::ethereum::create_account(&client, "quorum").await?;
quorum_vault_client::api::ethereum::list_accounts(&client, "quorum").await?;
quorum_vault_client::api::ethereum::read_account(&client, "quorum", address).await?;
quorum_vault_client::api::ethereum::sign_transaction(&client, "quorum", chain_id, tx).await?;
quorum_vault_client::api::ethereum::import_private_key(&client, "quorum", private_key).await?;
quorum_vault_client::api::ethereum::sign(&client, "quorum", address, data).await?;
// Keys
quorum_vault_client::api::keys::create_key(&client, "quorum", id, algorithm, tags).await?;
quorum_vault_client::api::keys::read_key(&client, "quorum", id).await?;
quorum_vault_client::api::keys::list_keys(&client, "quorum").await?;
quorum_vault_client::api::keys::destroy_key(&client, "quorum", id).await?;
quorum_vault_client::api::keys::import_key(&client, "quorum", id, algorithm, tags, private_key).await?;
quorum_vault_client::api::keys::update_key_tags(&client, "quorum", id, tags).await?;
quorum_vault_client::api::keys::sign(&client, "quorum", id, data).await?;
quorum_vault_client::api::keys::sign_hash(&client, "quorum", id, hash).await?;
// ZK-SNARKs (new in v2.0)
quorum_vault_client::api::zksnarks::create_zksnarks_account(&client, "quorum").await?;
quorum_vault_client::api::zksnarks::read_zksnarks_account(&client, "quorum", id).await?;
quorum_vault_client::api::zksnarks::list_zksnarks_accounts(&client, "quorum").await?;
quorum_vault_client::api::zksnarks::zksnarks_sign(&client, "quorum", id, data).await?;
quorum_vault_client::api::zksnarks::zksnarks_sign_hash(&client, "quorum", id, hash).await?;- ZK-SNARKs support — Full API for creating, reading, listing zk-SNARKs (EdDSA/BabyJubJub) accounts and signing data.
signfunction for Ethereum — Sign arbitrary data with an Ethereum account.sign_hashfunctions — Both Keys and ZK-SNARKs modules now providesign_hashfor signing pre-computed 32-byte digests, bypassing internal keccak256 hashing.update_key_tags— Update metadata tags on an existing key.
Add the following to your Cargo.toml:
[dependencies]
quorum-vault-client = "2.0.0"The client is used to configure the connection to Vault and is required to be passed to all API calls for execution. Behind the scenes it uses an asynchronous client from Reqwest for communicating to Vault.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
fn main() {
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
}Create new Ethereum Wallet
The following example creates a new Ethereum Wallet in the Vault.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
// By default the plugin mounts the Ethereum backend at the path "quorum"
let created_account = quorum_vault_client::api::ethereum::create_account(&client, "quorum").await.unwrap();
println!("result: {:?}", created_account);
}Result of the execution is the following:
> result: EthereumAccountResponse { address: 0x1a669bad7bda1f553087df8568b8782bcb0023ac, compressed_public_key: "0x020e44fde7435da96f8260788a89d4c37f2b3d96fd936dd978b886de6872d73062", public_key: "0x040e44fde7435da96f8260788a89d4c37f2b3d96fd936dd978b886de6872d730629c94a4803d3073b0bbe9a3d46f201eef5beec04d0e6f464e07704c159edd2c64", namespace: "" }List all Ethereum Wallets
The following example gets list of all Ethereum Wallets in the Vault.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
let list_accounts = quorum_vault_client::api::ethereum::list_accounts(&client, "quorum").await.unwrap();
println!("result: {:?}", list_accounts);
}Result of the execution is the following:
> result: EthereumAccountsResponse { keys: [0x1a669bad7bda1f553087df8568b8782bcb0023ac, 0x8d3113e29cb92f44f1762e52d2a0276509b36b82] }Read Ethereum Wallet
The following example gets the Ethereum Wallet by address.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder, Address};
use std::str::FromStr;
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
let address = Address::from_str("0x8d3113e29CB92F44F1762E52D2a0276509b36b82").unwrap();
let read_account = quorum_vault_client::api::ethereum::read_account(&client, "quorum", address).await.unwrap();
println!("result: {:?}", read_account);
}Result of the execution is the following:
> result: EthereumAccountResponse { address: 0x8d3113e29cb92f44f1762e52d2a0276509b36b82, compressed_public_key: "0x03b1c069a45b14697567661e6426ab0639f73762d7526765b2bd6891a73d84ebb5", public_key: "0x04b1c069a45b14697567661e6426ab0639f73762d7526765b2bd6891a73d84ebb57e6abbec4c9738a025d1a611e431ecf006227dbf6ca400f85518df70e5d101cb", namespace: "" }Sign Ethereum Transaction
The following example signs an Ethereum Transaction.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder, TransactionRequest, Address, U256};
use std::str::FromStr;
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
let address = Address::from_str("0x8d3113e29CB92F44F1762E52D2a0276509b36b82").unwrap();
let tx = TransactionRequest::default()
.from(address)
.to(address)
.value(U256::from_str("1000000000000000000").unwrap())
.gas_limit(21000)
.gas_price(1)
.nonce(0);
let sign_transaction = quorum_vault_client::api::ethereum::sign_transaction(&client, "quorum", 1, tx).await.unwrap();
println!("result: {:?}", sign_transaction);
}Result of the execution is the following:
> result: EthereumSignTransactionResponse { signature: "0xf29001752503d05ae83874193a8d866d49fc897c1a2fcb6229a0c61e4b5663f7097817a26f4c6014bbfd24c484bad9587c9c627c6f70d020f8638a4067bb78e801" }Import Private Key
The following example imports an existing private key into the Vault as a new Ethereum account.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder, B256};
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
let private_key: B256 = "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
.parse()
.unwrap();
let account = quorum_vault_client::api::ethereum::import_private_key(&client, "quorum", private_key).await.unwrap();
println!("result: {:?}", account);
}Result of the execution is the following:
> result: EthereumAccountResponse { address: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266, compressed_public_key: "0x...", public_key: "0x...", namespace: "" }Sign Arbitrary Data
The following example signs arbitrary data with an Ethereum account.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder, Address};
use std::str::FromStr;
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
let address = Address::from_str("0x8d3113e29CB92F44F1762E52D2a0276509b36b82").unwrap();
let signature = quorum_vault_client::api::ethereum::sign(&client, "quorum", address, b"some-data").await.unwrap();
println!("result: {:?}", signature);
}Result of the execution is the following:
> result: EthereumSignResponse { signature: "0x..." }Create Key
The following example creates a new key in the Vault.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
use quorum_vault_client::api::keys::KeyCryptoAlgorithm;
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).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);
}Result of the execution is the following:
> result: KeyResponse { created_at: "2023-01-30T09:08:22.217224856Z", curve: "secp256k1", id: "some-id", namespace: "", public_key: "BIwm5UiSGTiXVRlB_rS7qYSzQ6XZbaWfUOJKVicU85q-N7zuAak2JQfAHUs2Sm2WAA7YyWdN7_4UFJFggEa6AKw=", signing_algorithm: "ecdsa", tags: {"tag": "value"}, updated_at: "2023-01-30T09:08:22.217224856Z", version: 1 }Read Key
The following example reads the key by id.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
let key = quorum_vault_client::api::keys::read_key(&client, "quorum", "some-id").await.unwrap();
println!("result: {:?}", key);
}Result of the execution is the following:
> result: KeyResponse { created_at: "2023-01-30T09:08:22.217224856Z", curve: "secp256k1", id: "some-id", namespace: "", public_key: "BIwm5UiSGTiXVRlB_rS7qYSzQ6XZbaWfUOJKVicU85q-N7zuAak2JQfAHUs2Sm2WAA7YyWdN7_4UFJFggEa6AKw=", signing_algorithm: "ecdsa", tags: {"tag": "value"}, updated_at: "2023-01-30T09:08:22.217224856Z", version: 1 }List Keys
The following example lists all keys in the Vault.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
let keys = quorum_vault_client::api::keys::list_keys(&client, "quorum").await.unwrap();
println!("result: {:?}", keys);
}Result of the execution is the following:
> result: KeysResponse { keys: ["some-id"] }Delete Key
The following example deletes the key by id.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
quorum_vault_client::api::keys::destroy_key(&client, "quorum", "some-id").await.unwrap();
}Sign Data
The following example signs data with a key by its id. The data is hashed with keccak256 before signing.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
let signature = quorum_vault_client::api::keys::sign(&client, "quorum", "some-id", b"some-data").await.unwrap();
println!("signature: {:?}", signature);
}Result of the execution is the following:
> signature: SignResponse { signature: "Z1ibkBIGjMLh5pSR5mFZ5NbesrM57g-FGkFr0sbIyIlI_M0BYVN_LD-Nt7x1wUo6AoLQyL0I-z7PD8MsdgmkhQ==" }Import Private Key
The following example imports an existing private key into the Vault.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
use quorum_vault_client::api::keys::KeyCryptoAlgorithm;
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
let key = quorum_vault_client::api::keys::import_key(
&client,
"quorum",
"some-id",
KeyCryptoAlgorithm::Secp256k1,
[("tag".to_string(), "value".to_string())].into_iter().collect(),
"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
).await.unwrap();
println!("result: {:?}", key);
}Result of the execution is the following:
> result: KeyResponse { created_at: "2023-01-30T09:08:22.217224856Z", curve: "secp256k1", id: "some-id", namespace: "", public_key: "BIwm5UiSGTiXVRlB_rS7qYSzQ6XZbaWfUOJKVicU85q-N7zuAak2JQfAHUs2Sm2WAA7YyWdN7_4UFJFggEa6AKw=", signing_algorithm: "ecdsa", tags: {"tag": "value"}, updated_at: "2023-01-30T09:08:22.217224856Z", version: 1 }Update Key Tags
The following example updates the metadata tags of an existing key.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
let key = quorum_vault_client::api::keys::update_key_tags(
&client,
"quorum",
"some-id",
[("env".to_string(), "production".to_string())].into_iter().collect(),
).await.unwrap();
println!("result: {:?}", key);
}Result of the execution is the following:
> result: KeyResponse { created_at: "2023-01-30T09:08:22.217224856Z", curve: "secp256k1", id: "some-id", namespace: "", public_key: "BIwm5UiSGTiXVRlB_rS7qYSzQ6XZbaWfUOJKVicU85q-N7zuAak2JQfAHUs2Sm2WAA7YyWdN7_4UFJFggEa6AKw=", signing_algorithm: "ecdsa", tags: {"env": "production"}, updated_at: "2023-01-30T09:08:22.217224856Z", version: 2 }Create ZK-SNARKs Account
The following example creates a new ZK-SNARKs (EdDSA/BabyJubJub) account in the Vault.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
let account = quorum_vault_client::api::zksnarks::create_zksnarks_account(&client, "quorum").await.unwrap();
println!("result: {:?}", account);
}Result of the execution is the following:
> result: ZkSnarksAccountResponse { curve: "babyjubjub", namespace: "", public_key: "...", signing_algorithm: "eddsa" }Read ZK-SNARKs Account
The following example reads a ZK-SNARKs account by its public key, which is used as the account id.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
let account = quorum_vault_client::api::zksnarks::read_zksnarks_account(&client, "quorum", "<public-key>").await.unwrap();
println!("result: {:?}", account);
}Result of the execution is the following:
> result: ZkSnarksAccountResponse { curve: "babyjubjub", namespace: "", public_key: "...", signing_algorithm: "eddsa" }List ZK-SNARKs Accounts
The following example lists all ZK-SNARKs accounts in the Vault.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
let accounts = quorum_vault_client::api::zksnarks::list_zksnarks_accounts(&client, "quorum").await.unwrap();
println!("result: {:?}", accounts);
}Result of the execution is the following:
> result: ZkSnarksAccountsResponse { keys: ["<public-key-1>", "<public-key-2>"] }Sign Data
The following example signs data with a ZK-SNARKs account. The data is hashed with keccak256 before signing.
use quorum_vault_client::{Client, VaultClient, VaultClientSettingsBuilder};
#[tokio::main]
async fn main() {
// Create a client
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address("https://127.0.0.1:8200")
.token("TOKEN")
.build()
.unwrap()
).unwrap();
let signature = quorum_vault_client::api::zksnarks::zksnarks_sign(&client, "quorum", "<public-key>", b"some-data").await.unwrap();
println!("signature: {:?}", signature);
}Result of the execution is the following:
> signature: ZkSnarksSignResponse { signature: "..." }