Skip to content

Commit

Permalink
support send_message_signed
Browse files Browse the repository at this point in the history
  • Loading branch information
37ng committed Feb 4, 2024
1 parent b8edb54 commit 330402a
Show file tree
Hide file tree
Showing 11 changed files with 1,706 additions and 385 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions gateway-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ edition = "2021"
serde.workspace = true
lib-didethresolver.workspace = true
ethers.workspace = true
thiserror.workspace = true
jsonrpsee.workspace = true
19 changes: 19 additions & 0 deletions gateway-types/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use ethers::{
abi::EncodePackedError,
contract::ContractError,
providers::{Middleware, ProviderError},
signers::WalletError,
};
use thiserror::Error;

#[derive(Error, Debug)]

Check warning on line 9 in gateway-types/src/error.rs

View check run for this annotation

Codecov / codecov/patch

gateway-types/src/error.rs#L9

Added line #L9 was not covered by tests
pub enum ExtSignerError<M: Middleware> {
#[error(transparent)]
Encode(#[from] EncodePackedError),
#[error("{0}")]
ContractError(#[from] ContractError<M>),
#[error(transparent)]
Provider(#[from] ProviderError),
#[error(transparent)]
Wallet(#[from] WalletError),
}
7 changes: 6 additions & 1 deletion gateway-types/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Shared types between XPS Gateawy and client (libxmtp)
pub mod error;

use ethers::prelude::Bytes;
use ethers::types::{Address, Bytes, Signature};
use serde::{Deserialize, Serialize};

/// Address of the did:ethr Registry on Sepolia
Expand All @@ -16,6 +17,10 @@ pub struct Message {
pub conversation_id: [u8; 32],
/// message content in bytes
pub payload: Bytes,
// Sender's identity
pub identity: Address,
// Signature by sender
pub signature: Signature,
}

/// GrantInstallationResult represents the result of a grant installation operation in the DID registry.
Expand Down
1 change: 1 addition & 0 deletions messaging/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ async-trait.workspace = true
ethers = { workspace = true, features = ["ws"] }
serde.workspace = true
thiserror.workspace = true
gateway-types.workspace = true
1,939 changes: 1,575 additions & 364 deletions messaging/abi/Conversation.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions messaging/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ use ethers::{
};
use thiserror::Error;

use std::num::TryFromIntError;

#[derive(Error, Debug)]

Check warning on line 9 in messaging/src/error.rs

View check run for this annotation

Codecov / codecov/patch

messaging/src/error.rs#L9

Added line #L9 was not covered by tests
pub enum MessagingOperationError<M: Middleware> {
#[error(transparent)]
ContractError(#[from] ContractError<M>),
#[error(transparent)]
ProviderError(#[from] ProviderError),
#[error("Error converting from int: {0}")]
IntConversion(#[from] TryFromIntError),
}
78 changes: 65 additions & 13 deletions messaging/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
pub mod error;

use error::MessagingOperationError;
use ethers::contract::abigen;
use ethers::core::types::Bytes;
use ethers::providers::Middleware;
use ethers::{
abi::{Address, Token},
contract::abigen,
core::abi::encode_packed,
core::types::{Bytes, Signature},
providers::Middleware,
signers::LocalWallet,
types::H256,
utils::keccak256,
};
use gateway_types::{error::ExtSignerError, Message};

abigen!(
Conversation,

Check warning on line 17 in messaging/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

messaging/src/lib.rs#L17

Added line #L17 was not covered by tests
Expand All @@ -12,28 +20,72 @@ abigen!(
);

pub struct MessagingOperations<Middleware> {
messaging: Conversation<Middleware>,
contract: Conversation<Middleware>,
}

impl<M> MessagingOperations<M>
where
M: Middleware + 'static,
{
/// Creates a new MessagingOperations instance
pub fn new(messaging: Conversation<M>) -> Self {
Self { messaging }
pub fn new(contract: Conversation<M>) -> Self {
Self { contract }
}

pub async fn send_message(
&self,
conversation_id: [u8; 32],
payload: Bytes,
) -> Result<(), MessagingOperationError<M>> {
self.messaging
.send_message(conversation_id, payload)
pub async fn send_message(&self, m: Message) -> Result<(), MessagingOperationError<M>> {
self.contract
.send_message_signed(
m.conversation_id,
m.payload,
m.identity,
m.signature.v.try_into()?,
m.signature.r.into(),
m.signature.s.into(),
)
.send()
.await?
.await?;
Ok(())
}
}

/// Signer for data that is externally signed to be processed by the Conversation Contract.
#[async_trait::async_trait]
pub trait ConversationSignerExt {
/// Sign hash of the data for [`Conversation::send_message_signed`]
async fn sign_xmtp_message<M: Middleware>(
&self,
conversation: &Conversation<M>,
conversation_id: [u8; 32],
payload: Bytes,
identity: Address,
) -> Result<Signature, ExtSignerError<M>>;
}

#[async_trait::async_trait]
impl ConversationSignerExt for LocalWallet {
async fn sign_xmtp_message<M: Middleware>(
&self,
conversation: &Conversation<M>,
conversation_id: [u8; 32],
payload: Bytes,
identity: Address,
) -> Result<Signature, ExtSignerError<M>> {
let nonce = conversation.nonce(identity).call().await?;
let mut nonce_bytes = [0; 32];
nonce.to_big_endian(&mut nonce_bytes);
let tokens = vec![
Token::FixedBytes(vec![0x19]),
Token::FixedBytes(vec![0x0]),
Token::FixedBytes(conversation_id[0..32].to_vec()),
Token::Bytes(payload.to_vec()),
Token::Address(identity),
Token::Bytes(nonce_bytes[0..32].to_vec()),
];

let encoded = encode_packed(tokens.as_slice())?;
let digest = H256(keccak256(encoded));
let signature = self.sign_hash(digest)?;
Ok(signature)
}
}
1 change: 0 additions & 1 deletion xps-gateway/src/rpc/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use lib_didethresolver::types::XmtpAttribute;
#[rpc(server, client, namespace = "xps")]
pub trait Xps {
/// Send message using gateway's signer
// TODO: Change the [method](https://github.com/xmtp/xps-contract/blob/main/contracts/Conversation.sol#L56) to public and support it here - i.e. messages handled in gateway must include signature.
#[method(name = "sendMessage")]
async fn send_message(&self, _message: Message) -> Result<(), ErrorObjectOwned>;

Expand Down
2 changes: 1 addition & 1 deletion xps-gateway/src/rpc/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl<P: Middleware + 'static> XpsServer for XpsMethods<P> {
async fn send_message(&self, message: Message) -> Result<(), ErrorObjectOwned> {
let result = self
.message_operations
.send_message(message.conversation_id, message.payload)
.send_message(message)
.await
.map_err(RpcError::from)?;

Expand Down
35 changes: 30 additions & 5 deletions xps-gateway/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ mod integration_util;

use anyhow::Error;

use ethers::{signers::LocalWallet, types::Bytes};
use ethers::{signers::LocalWallet, types::Bytes, utils::keccak256};
use lib_didethresolver::{
did_registry::RegistrySignerExt,
types::{DidUrl, KeyEncoding, XmtpAttribute, XmtpKeyPurpose},
};
use messaging::ConversationSignerExt;
use xps_gateway::rpc::XpsClient;

use ethers::middleware::Middleware;
Expand All @@ -27,13 +28,37 @@ async fn test_say_hello() -> Result<(), Error> {

#[tokio::test]
async fn test_send_message() -> Result<(), Error> {
with_xps_client(None, |client, _, _, _| async move {
with_xps_client(None, |client, context, _resolver, anvil| async move {
let wallet: LocalWallet = anvil.keys()[3].clone().into();
let me = get_user(&anvil, 3).await;

let conversation_id = keccak256(b"conversation_id");
let payload = Bytes::from_static(b"payload");

let signature = wallet
.sign_xmtp_message(
&context.conversation,
conversation_id,
payload.clone(),
me.address(),
)
.await?;

let message = Message {
conversation_id: [0; 32],
payload: Bytes::from_static(b"payload"),
conversation_id: conversation_id,
payload: payload,
identity: me.address(),
signature: signature,
};

let pre_nonce = context.conversation.nonce(me.address()).call().await?;
assert!(pre_nonce == U256::zero());

let result = client.send_message(message).await;
assert!(!result.is_err());
assert!(result.is_ok());

let post_nonce = context.conversation.nonce(me.address()).call().await?;
assert!(post_nonce == pre_nonce + 1);
Ok(())
})
.await
Expand Down

0 comments on commit 330402a

Please sign in to comment.