Skip to content

Commit

Permalink
Update Node and WASM bindings (#1333)
Browse files Browse the repository at this point in the history
* Move signature fns to signatures mod

* Make DB path optional

* Add tests

* Prepare releases
  • Loading branch information
rygine authored Nov 22, 2024
1 parent 363daec commit ce01a73
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 102 deletions.
4 changes: 4 additions & 0 deletions bindings_node/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# @xmtp/node-bindings

## 0.0.22

- Moved `verify_signed_with_public_key` out of `Client`

## 0.0.21

- Added `installation_id_bytes` to `Client`
Expand Down
2 changes: 1 addition & 1 deletion bindings_node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@xmtp/node-bindings",
"version": "0.0.21",
"version": "0.0.22",
"repository": {
"type": "git",
"url": "git+https://[email protected]/xmtp/libxmtp.git",
Expand Down
52 changes: 5 additions & 47 deletions bindings_node/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use tracing_subscriber::{fmt, prelude::*};
pub use xmtp_api_grpc::grpc_api_helper::Client as TonicApiClient;
use xmtp_cryptography::signature::ed25519_public_key_to_address;
use xmtp_id::associations::builder::SignatureRequest;
use xmtp_id::associations::verify_signed_with_public_context;
use xmtp_mls::builder::ClientBuilder;
use xmtp_mls::groups::scoped_client::LocalScopedGroupClient;
use xmtp_mls::identity::IdentityStrategy;
Expand Down Expand Up @@ -123,7 +122,7 @@ fn init_logging(options: LogOptions) -> Result<()> {
pub async fn create_client(
host: String,
is_secure: bool,
db_path: String,
db_path: Option<String>,
inbox_id: String,
account_address: String,
encryption_key: Option<Uint8Array>,
Expand All @@ -135,7 +134,10 @@ pub async fn create_client(
.await
.map_err(|_| Error::from_reason("Error creating Tonic API client"))?;

let storage_option = StorageOption::Persistent(db_path);
let storage_option = match db_path {
Some(path) => StorageOption::Persistent(path),
None => StorageOption::Ephemeral,
};

let store = match encryption_key {
Some(key) => {
Expand Down Expand Up @@ -295,48 +297,4 @@ impl Client {
.map_err(ErrorWrapper::from)?;
Ok(state.into_iter().map(Into::into).collect())
}

#[napi]
pub fn sign_with_installation_key(&self, signature_text: String) -> Result<Uint8Array> {
let result = self
.inner_client
.context()
.sign_with_public_context(signature_text)
.map_err(ErrorWrapper::from)?;

Ok(result.into())
}

#[napi]
pub fn verify_signed_with_installation_key(
&self,
signature_text: String,
signature_bytes: Uint8Array,
) -> Result<()> {
let public_key = self.inner_client().installation_public_key();
self.verify_signed_with_public_key(signature_text, signature_bytes, public_key.into())
}

#[napi]
pub fn verify_signed_with_public_key(
&self,
signature_text: String,
signature_bytes: Uint8Array,
public_key: Uint8Array,
) -> Result<()> {
let signature_bytes = signature_bytes.deref().to_vec();
let signature_bytes: [u8; 64] = signature_bytes
.try_into()
.map_err(|_| Error::from_reason("signature_bytes is not 64 bytes long."))?;

let public_key = public_key.deref().to_vec();
let public_key: [u8; 32] = public_key
.try_into()
.map_err(|_| Error::from_reason("public_key is not 32 bytes long."))?;

Ok(
verify_signed_with_public_context(signature_text, &signature_bytes, &public_key)
.map_err(ErrorWrapper::from)?,
)
}
}
45 changes: 44 additions & 1 deletion bindings_node/src/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,31 @@ use napi_derive::napi;
use std::ops::Deref;
use xmtp_id::associations::{
unverified::{NewUnverifiedSmartContractWalletSignature, UnverifiedSignature},
AccountId,
verify_signed_with_public_context, AccountId,
};

#[napi]
pub fn verify_signed_with_public_key(
signature_text: String,
signature_bytes: Uint8Array,
public_key: Uint8Array,
) -> Result<()> {
let signature_bytes = signature_bytes.deref().to_vec();
let signature_bytes: [u8; 64] = signature_bytes
.try_into()
.map_err(|_| Error::from_reason("signature_bytes is not 64 bytes long."))?;

let public_key = public_key.deref().to_vec();
let public_key: [u8; 32] = public_key
.try_into()
.map_err(|_| Error::from_reason("public_key is not 32 bytes long."))?;

Ok(
verify_signed_with_public_context(signature_text, &signature_bytes, &public_key)
.map_err(ErrorWrapper::from)?,
)
}

#[napi]
#[derive(Eq, Hash, PartialEq)]
pub enum SignatureRequestType {
Expand Down Expand Up @@ -167,4 +189,25 @@ impl Client {

Ok(())
}

#[napi]
pub fn sign_with_installation_key(&self, signature_text: String) -> Result<Uint8Array> {
let result = self
.inner_client()
.context()
.sign_with_public_context(signature_text)
.map_err(ErrorWrapper::from)?;

Ok(result.into())
}

#[napi]
pub fn verify_signed_with_installation_key(
&self,
signature_text: String,
signature_bytes: Uint8Array,
) -> Result<()> {
let public_key = self.inner_client().installation_public_key();
verify_signed_with_public_key(signature_text, signature_bytes, public_key.into())
}
}
28 changes: 26 additions & 2 deletions bindings_node/test/Client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { v4 } from 'uuid'
import { toBytes } from 'viem'
import { describe, expect, it } from 'vitest'
import { createClient, createRegisteredClient, createUser } from '@test/helpers'
import { ConsentEntityType, ConsentState, SignatureRequestType } from '../dist'
import {
ConsentEntityType,
ConsentState,
SignatureRequestType,
verifySignedWithPublicKey,
} from '../dist'

describe('Client', () => {
it('should not be registered at first', async () => {
Expand Down Expand Up @@ -224,5 +229,24 @@ describe('Client', () => {
user2.account.address.toLowerCase(),
])
})
it('should create client with structured logging', async () => {})

it('should sign and verify with installation key', async () => {
const user = createUser()
const client = await createRegisteredClient(user)
const text = 'gm!'
const signature = client.signWithInstallationKey(text)
expect(signature).toBeDefined()
expect(() =>
client.verifySignedWithInstallationKey(text, signature)
).not.toThrow()
expect(() =>
client.verifySignedWithInstallationKey(text, new Uint8Array())
).toThrow()
expect(() =>
verifySignedWithPublicKey(text, signature, client.installationIdBytes())
).not.toThrow()
expect(() =>
verifySignedWithPublicKey(text, signature, new Uint8Array())
).toThrow()
})
})
4 changes: 4 additions & 0 deletions bindings_wasm/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# @xmtp/wasm-bindings

## 0.0.7

- Moved `verify_signed_with_public_key` out of `Client`

## 0.0.6

- Added `installation_id_bytes` to `Client`
Expand Down
2 changes: 1 addition & 1 deletion bindings_wasm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@xmtp/wasm-bindings",
"version": "0.0.6",
"version": "0.0.7",
"type": "module",
"license": "MIT",
"description": "WASM bindings for the libXMTP rust library",
Expand Down
54 changes: 5 additions & 49 deletions bindings_wasm/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use wasm_bindgen::JsValue;
use xmtp_api_http::XmtpHttpApiClient;
use xmtp_cryptography::signature::ed25519_public_key_to_address;
use xmtp_id::associations::builder::SignatureRequest;
use xmtp_id::associations::verify_signed_with_public_context;
use xmtp_mls::builder::ClientBuilder;
use xmtp_mls::groups::scoped_client::ScopedGroupClient;
use xmtp_mls::identity::IdentityStrategy;
Expand Down Expand Up @@ -125,7 +124,7 @@ pub async fn create_client(
host: String,
inbox_id: String,
account_address: String,
db_path: String,
db_path: Option<String>,
encryption_key: Option<Uint8Array>,
history_sync_url: Option<String>,
log_options: Option<LogOptions>,
Expand All @@ -134,7 +133,10 @@ pub async fn create_client(
xmtp_mls::storage::init_sqlite().await;
let api_client = XmtpHttpApiClient::new(host.clone()).unwrap();

let storage_option = StorageOption::Persistent(db_path);
let storage_option = match db_path {
Some(path) => StorageOption::Persistent(path),
None => StorageOption::Ephemeral,
};

let store = match encryption_key {
Some(key) => {
Expand Down Expand Up @@ -284,50 +286,4 @@ impl Client {
pub fn conversations(&self) -> Conversations {
Conversations::new(self.inner_client.clone())
}

#[wasm_bindgen(js_name = signWithInstallationKey)]
pub fn sign_with_installation_key(&self, signature_text: String) -> Result<Uint8Array, JsError> {
let result = self
.inner_client
.context()
.sign_with_public_context(signature_text)
.map_err(|e| JsError::new(format!("{}", e).as_str()))?;

Ok(Uint8Array::from(result.as_slice()))
}

#[wasm_bindgen(js_name = verifySignedWithInstallationKey)]
pub fn verify_signed_with_installation_key(
&self,
signature_text: String,
signature_bytes: Uint8Array,
) -> Result<(), JsError> {
let public_key = self.inner_client().installation_public_key();
self.verify_signed_with_public_key(
signature_text,
signature_bytes,
Uint8Array::from(public_key.as_slice()),
)
}

#[wasm_bindgen(js_name = verifySignedWithPublicKey)]
pub fn verify_signed_with_public_key(
&self,
signature_text: String,
signature_bytes: Uint8Array,
public_key: Uint8Array,
) -> Result<(), JsError> {
let signature_bytes = signature_bytes.to_vec();
let signature_bytes: [u8; 64] = signature_bytes
.try_into()
.map_err(|_| JsError::new("signature_bytes is not 64 bytes long."))?;

let public_key = public_key.to_vec();
let public_key: [u8; 32] = public_key
.try_into()
.map_err(|_| JsError::new("public_key is not 32 bytes long."))?;

verify_signed_with_public_context(signature_text, &signature_bytes, &public_key)
.map_err(|e| JsError::new(format!("{}", e).as_str()))
}
}
46 changes: 46 additions & 0 deletions bindings_wasm/src/signatures.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
use js_sys::Uint8Array;
use wasm_bindgen::prelude::{wasm_bindgen, JsError};
use xmtp_id::associations::verify_signed_with_public_context;
use xmtp_id::associations::{
unverified::{NewUnverifiedSmartContractWalletSignature, UnverifiedSignature},
AccountId,
};

use crate::client::Client;

#[wasm_bindgen(js_name = verifySignedWithPublicKey)]
pub fn verify_signed_with_public_key(
signature_text: String,
signature_bytes: Uint8Array,
public_key: Uint8Array,
) -> Result<(), JsError> {
let signature_bytes = signature_bytes.to_vec();
let signature_bytes: [u8; 64] = signature_bytes
.try_into()
.map_err(|_| JsError::new("signature_bytes is not 64 bytes long."))?;

let public_key = public_key.to_vec();
let public_key: [u8; 32] = public_key
.try_into()
.map_err(|_| JsError::new("public_key is not 32 bytes long."))?;

verify_signed_with_public_context(signature_text, &signature_bytes, &public_key)
.map_err(|e| JsError::new(format!("{}", e).as_str()))
}

#[wasm_bindgen]
#[derive(Clone, Eq, Hash, PartialEq)]
pub enum SignatureRequestType {
Expand Down Expand Up @@ -172,4 +193,29 @@ impl Client {

Ok(())
}

#[wasm_bindgen(js_name = signWithInstallationKey)]
pub fn sign_with_installation_key(&self, signature_text: String) -> Result<Uint8Array, JsError> {
let result = self
.inner_client()
.context()
.sign_with_public_context(signature_text)
.map_err(|e| JsError::new(format!("{}", e).as_str()))?;

Ok(Uint8Array::from(result.as_slice()))
}

#[wasm_bindgen(js_name = verifySignedWithInstallationKey)]
pub fn verify_signed_with_installation_key(
&self,
signature_text: String,
signature_bytes: Uint8Array,
) -> Result<(), JsError> {
let public_key = self.inner_client().installation_public_key();
verify_signed_with_public_key(
signature_text,
signature_bytes,
Uint8Array::from(public_key.as_slice()),
)
}
}
2 changes: 1 addition & 1 deletion bindings_wasm/tests/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub async fn test_create_client() {
host.clone(),
inbox_id.unwrap(),
account_address.clone(),
"test".to_string(),
None,
None,
None,
Some(LogOptions {
Expand Down

0 comments on commit ce01a73

Please sign in to comment.