Interactive CLI for demo (#17)

* delete pks

* refactor

* update ciphersuite

* add draft cli interface

* add ui

* change to read file

* fix problem with threads, add error handler

* add graceful shutdown
This commit is contained in:
Ekaterina Broslavskaya
2024-07-18 11:52:39 +03:00
committed by GitHub
parent 62f15d2381
commit 4774e574d2
16 changed files with 1544 additions and 1848 deletions

View File

@@ -18,8 +18,12 @@ openmls_rust_crypto = "=0.2.0"
openmls_traits = "=0.2.0"
# waku-bindings = "0.6.0"
bus = "=2.4.1"
tokio = { version = "=1.38.0", features = ["macros", "rt-multi-thread"] }
tokio = { version = "=1.38.0", features = [
"macros",
"rt-multi-thread",
"full",
] }
tokio-util = "=0.7.11"
alloy = { git = "https://github.com/alloy-rs/alloy", features = [
"providers",
"node-bindings",
@@ -27,14 +31,24 @@ alloy = { git = "https://github.com/alloy-rs/alloy", features = [
"transports",
"k256",
] }
fred = { version = "=9.0.3", features = ["subscriber-client"] }
rand = "=0.8.5"
serde_json = "=1.0"
url = "=2.5.2"
tls_codec = "=0.3.0"
hex = "=0.4.3"
anyhow = "=1.0.71"
shlex = "=1.3.0"
clap = { version = "=4.5.8", features = ["derive"] }
anyhow = "=1.0.81"
thiserror = "=1.0.61"
crossterm = "=0.27.0"
ratatui = "=0.27.0"
textwrap = "=0.16.1"
ds = { path = "ds" }
sc_key_store = { path = "sc_key_store" }
mls_crypto = { path = "mls_crypto" }

File diff suppressed because one or more lines are too long

View File

@@ -127,8 +127,8 @@ pub mod DeploymentConfig {
b"`\x80`@R4\x80\x15`\x0FW`\0\x80\xFD[P`\x046\x10`FW`\x005`\xE0\x1C\x80c\x12\x90\r\xA8\x14`KW\x80c\xD7\xB6WE\x14`\x8FW\x80c\xF8\xA8\xFDm\x14`\xD2W\x80c\xF8\xCC\xBFG\x14`\xD4W[`\0\x80\xFD[`@\x80Q` \x80\x82\x01\x83R`\0\x90\x91R\x81Q\x80\x82\x01\x83R`\x0ETs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x90\x81\x90R\x91Q\x91\x82R\x01[`@Q\x80\x91\x03\x90\xF3[`\rT`\xAE\x90s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x81V[`@Qs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x90\x91\x16\x81R` \x01`\x86V[\0[`\x0CT`\xE0\x90`\xFF\x16\x81V[`@Q\x90\x15\x15\x81R` \x01`\x86V",
);
/**```solidity
struct NetworkConfig { address deployer; }
```*/
struct NetworkConfig { address deployer; }
```*/
#[allow(non_camel_case_types, non_snake_case)]
#[derive(Clone)]
pub struct NetworkConfig {
@@ -143,9 +143,7 @@ struct NetworkConfig { address deployer; }
type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,);
#[cfg(test)]
#[allow(dead_code, unreachable_patterns)]
fn _type_assertion(
_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>,
) {
fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
match _t {
alloy_sol_types::private::AssertTypeEq::<
<UnderlyingSolTuple as alloy_sol_types::SolType>::RustType,
@@ -182,49 +180,37 @@ struct NetworkConfig { address deployer; }
}
#[inline]
fn stv_abi_encoded_size(&self) -> usize {
let tuple = <UnderlyingRustTuple<
'_,
> as ::core::convert::From<Self>>::from(self.clone());
<UnderlyingSolTuple<
'_,
> as alloy_sol_types::SolType>::abi_encoded_size(&tuple)
let tuple =
<UnderlyingRustTuple<'_> as ::core::convert::From<Self>>::from(self.clone());
<UnderlyingSolTuple<'_> as alloy_sol_types::SolType>::abi_encoded_size(&tuple)
}
#[inline]
fn stv_eip712_data_word(&self) -> alloy_sol_types::Word {
<Self as alloy_sol_types::SolStruct>::eip712_hash_struct(self)
}
#[inline]
fn stv_abi_encode_packed_to(
&self,
out: &mut alloy_sol_types::private::Vec<u8>,
) {
let tuple = <UnderlyingRustTuple<
'_,
> as ::core::convert::From<Self>>::from(self.clone());
<UnderlyingSolTuple<
'_,
> as alloy_sol_types::SolType>::abi_encode_packed_to(&tuple, out)
fn stv_abi_encode_packed_to(&self, out: &mut alloy_sol_types::private::Vec<u8>) {
let tuple =
<UnderlyingRustTuple<'_> as ::core::convert::From<Self>>::from(self.clone());
<UnderlyingSolTuple<'_> as alloy_sol_types::SolType>::abi_encode_packed_to(
&tuple, out,
)
}
}
#[automatically_derived]
impl alloy_sol_types::SolType for NetworkConfig {
type RustType = Self;
type Token<'a> = <UnderlyingSolTuple<
'a,
> as alloy_sol_types::SolType>::Token<'a>;
type Token<'a> = <UnderlyingSolTuple<'a> as alloy_sol_types::SolType>::Token<'a>;
const SOL_NAME: &'static str = <Self as alloy_sol_types::SolStruct>::NAME;
const ENCODED_SIZE: Option<usize> = <UnderlyingSolTuple<
'_,
> as alloy_sol_types::SolType>::ENCODED_SIZE;
const ENCODED_SIZE: Option<usize> =
<UnderlyingSolTuple<'_> as alloy_sol_types::SolType>::ENCODED_SIZE;
#[inline]
fn valid_token(token: &Self::Token<'_>) -> bool {
<UnderlyingSolTuple<'_> as alloy_sol_types::SolType>::valid_token(token)
}
#[inline]
fn detokenize(token: Self::Token<'_>) -> Self::RustType {
let tuple = <UnderlyingSolTuple<
'_,
> as alloy_sol_types::SolType>::detokenize(token);
let tuple = <UnderlyingSolTuple<'_> as alloy_sol_types::SolType>::detokenize(token);
<Self as ::core::convert::From<UnderlyingRustTuple<'_>>>::from(tuple)
}
}
@@ -233,14 +219,12 @@ struct NetworkConfig { address deployer; }
const NAME: &'static str = "NetworkConfig";
#[inline]
fn eip712_root_type() -> alloy_sol_types::private::Cow<'static, str> {
alloy_sol_types::private::Cow::Borrowed(
"NetworkConfig(address deployer)",
)
alloy_sol_types::private::Cow::Borrowed("NetworkConfig(address deployer)")
}
#[inline]
fn eip712_components() -> alloy_sol_types::private::Vec<
alloy_sol_types::private::Cow<'static, str>,
> {
fn eip712_components(
) -> alloy_sol_types::private::Vec<alloy_sol_types::private::Cow<'static, str>>
{
alloy_sol_types::private::Vec::new()
}
#[inline]
@@ -250,10 +234,10 @@ struct NetworkConfig { address deployer; }
#[inline]
fn eip712_encode_data(&self) -> alloy_sol_types::private::Vec<u8> {
<alloy::sol_types::sol_data::Address as alloy_sol_types::SolType>::eip712_data_word(
&self.deployer,
)
.0
.to_vec()
&self.deployer,
)
.0
.to_vec()
}
}
#[automatically_derived]
@@ -270,33 +254,24 @@ struct NetworkConfig { address deployer; }
rust: &Self::RustType,
out: &mut alloy_sol_types::private::Vec<u8>,
) {
out.reserve(
<Self as alloy_sol_types::EventTopic>::topic_preimage_length(rust),
);
out.reserve(<Self as alloy_sol_types::EventTopic>::topic_preimage_length(rust));
<alloy::sol_types::sol_data::Address as alloy_sol_types::EventTopic>::encode_topic_preimage(
&rust.deployer,
out,
);
}
#[inline]
fn encode_topic(
rust: &Self::RustType,
) -> alloy_sol_types::abi::token::WordToken {
fn encode_topic(rust: &Self::RustType) -> alloy_sol_types::abi::token::WordToken {
let mut out = alloy_sol_types::private::Vec::new();
<Self as alloy_sol_types::EventTopic>::encode_topic_preimage(
rust,
&mut out,
);
alloy_sol_types::abi::token::WordToken(
alloy_sol_types::private::keccak256(out),
)
<Self as alloy_sol_types::EventTopic>::encode_topic_preimage(rust, &mut out);
alloy_sol_types::abi::token::WordToken(alloy_sol_types::private::keccak256(out))
}
}
};
/**Custom error with signature `DeploymentConfig_InvalidDeployerAddress()` and selector `0x80585b44`.
```solidity
error DeploymentConfig_InvalidDeployerAddress();
```*/
```solidity
error DeploymentConfig_InvalidDeployerAddress();
```*/
#[allow(non_camel_case_types, non_snake_case)]
#[derive(Clone)]
pub struct DeploymentConfig_InvalidDeployerAddress {}
@@ -309,9 +284,7 @@ error DeploymentConfig_InvalidDeployerAddress();
type UnderlyingRustTuple<'a> = ();
#[cfg(test)]
#[allow(dead_code, unreachable_patterns)]
fn _type_assertion(
_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>,
) {
fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
match _t {
alloy_sol_types::private::AssertTypeEq::<
<UnderlyingSolTuple as alloy_sol_types::SolType>::RustType,
@@ -320,16 +293,14 @@ error DeploymentConfig_InvalidDeployerAddress();
}
#[automatically_derived]
#[doc(hidden)]
impl ::core::convert::From<DeploymentConfig_InvalidDeployerAddress>
for UnderlyingRustTuple<'_> {
impl ::core::convert::From<DeploymentConfig_InvalidDeployerAddress> for UnderlyingRustTuple<'_> {
fn from(value: DeploymentConfig_InvalidDeployerAddress) -> Self {
()
}
}
#[automatically_derived]
#[doc(hidden)]
impl ::core::convert::From<UnderlyingRustTuple<'_>>
for DeploymentConfig_InvalidDeployerAddress {
impl ::core::convert::From<UnderlyingRustTuple<'_>> for DeploymentConfig_InvalidDeployerAddress {
fn from(tuple: UnderlyingRustTuple<'_>) -> Self {
Self {}
}
@@ -337,9 +308,7 @@ error DeploymentConfig_InvalidDeployerAddress();
#[automatically_derived]
impl alloy_sol_types::SolError for DeploymentConfig_InvalidDeployerAddress {
type Parameters<'a> = UnderlyingSolTuple<'a>;
type Token<'a> = <Self::Parameters<
'a,
> as alloy_sol_types::SolType>::Token<'a>;
type Token<'a> = <Self::Parameters<'a> as alloy_sol_types::SolType>::Token<'a>;
const SIGNATURE: &'static str = "DeploymentConfig_InvalidDeployerAddress()";
const SELECTOR: [u8; 4] = [128u8, 88u8, 91u8, 68u8];
#[inline]
@@ -355,9 +324,9 @@ error DeploymentConfig_InvalidDeployerAddress();
}
};
/**Custom error with signature `DeploymentConfig_NoConfigForChain(uint256)` and selector `0x0b13dbff`.
```solidity
error DeploymentConfig_NoConfigForChain(uint256);
```*/
```solidity
error DeploymentConfig_NoConfigForChain(uint256);
```*/
#[allow(non_camel_case_types, non_snake_case)]
#[derive(Clone)]
pub struct DeploymentConfig_NoConfigForChain {
@@ -372,9 +341,7 @@ error DeploymentConfig_NoConfigForChain(uint256);
type UnderlyingRustTuple<'a> = (alloy::sol_types::private::U256,);
#[cfg(test)]
#[allow(dead_code, unreachable_patterns)]
fn _type_assertion(
_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>,
) {
fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
match _t {
alloy_sol_types::private::AssertTypeEq::<
<UnderlyingSolTuple as alloy_sol_types::SolType>::RustType,
@@ -383,16 +350,14 @@ error DeploymentConfig_NoConfigForChain(uint256);
}
#[automatically_derived]
#[doc(hidden)]
impl ::core::convert::From<DeploymentConfig_NoConfigForChain>
for UnderlyingRustTuple<'_> {
impl ::core::convert::From<DeploymentConfig_NoConfigForChain> for UnderlyingRustTuple<'_> {
fn from(value: DeploymentConfig_NoConfigForChain) -> Self {
(value._0,)
}
}
#[automatically_derived]
#[doc(hidden)]
impl ::core::convert::From<UnderlyingRustTuple<'_>>
for DeploymentConfig_NoConfigForChain {
impl ::core::convert::From<UnderlyingRustTuple<'_>> for DeploymentConfig_NoConfigForChain {
fn from(tuple: UnderlyingRustTuple<'_>) -> Self {
Self { _0: tuple.0 }
}
@@ -400,9 +365,7 @@ error DeploymentConfig_NoConfigForChain(uint256);
#[automatically_derived]
impl alloy_sol_types::SolError for DeploymentConfig_NoConfigForChain {
type Parameters<'a> = UnderlyingSolTuple<'a>;
type Token<'a> = <Self::Parameters<
'a,
> as alloy_sol_types::SolType>::Token<'a>;
type Token<'a> = <Self::Parameters<'a> as alloy_sol_types::SolType>::Token<'a>;
const SIGNATURE: &'static str = "DeploymentConfig_NoConfigForChain(uint256)";
const SELECTOR: [u8; 4] = [11u8, 19u8, 219u8, 255u8];
#[inline]
@@ -414,17 +377,17 @@ error DeploymentConfig_NoConfigForChain(uint256);
#[inline]
fn tokenize(&self) -> Self::Token<'_> {
(
<alloy::sol_types::sol_data::Uint<
256,
> as alloy_sol_types::SolType>::tokenize(&self._0),
<alloy::sol_types::sol_data::Uint<256> as alloy_sol_types::SolType>::tokenize(
&self._0,
),
)
}
}
};
/**Constructor`.
```solidity
constructor(address _broadcaster);
```*/
```solidity
constructor(address _broadcaster);
```*/
#[allow(non_camel_case_types, non_snake_case)]
#[derive(Clone)]
pub struct constructorCall {
@@ -439,9 +402,7 @@ constructor(address _broadcaster);
type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,);
#[cfg(test)]
#[allow(dead_code, unreachable_patterns)]
fn _type_assertion(
_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>,
) {
fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
match _t {
alloy_sol_types::private::AssertTypeEq::<
<UnderlyingSolTuple as alloy_sol_types::SolType>::RustType,
@@ -459,16 +420,16 @@ constructor(address _broadcaster);
#[doc(hidden)]
impl ::core::convert::From<UnderlyingRustTuple<'_>> for constructorCall {
fn from(tuple: UnderlyingRustTuple<'_>) -> Self {
Self { _broadcaster: tuple.0 }
Self {
_broadcaster: tuple.0,
}
}
}
}
#[automatically_derived]
impl alloy_sol_types::SolConstructor for constructorCall {
type Parameters<'a> = (alloy::sol_types::sol_data::Address,);
type Token<'a> = <Self::Parameters<
'a,
> as alloy_sol_types::SolType>::Token<'a>;
type Token<'a> = <Self::Parameters<'a> as alloy_sol_types::SolType>::Token<'a>;
#[inline]
fn new<'a>(
tuple: <Self::Parameters<'a> as alloy_sol_types::SolType>::RustType,
@@ -486,9 +447,9 @@ constructor(address _broadcaster);
}
};
/**Function with signature `IS_SCRIPT()` and selector `0xf8ccbf47`.
```solidity
function IS_SCRIPT() external view returns (bool);
```*/
```solidity
function IS_SCRIPT() external view returns (bool);
```*/
#[allow(non_camel_case_types, non_snake_case)]
#[derive(Clone)]
pub struct IS_SCRIPTCall {}
@@ -508,9 +469,7 @@ function IS_SCRIPT() external view returns (bool);
type UnderlyingRustTuple<'a> = ();
#[cfg(test)]
#[allow(dead_code, unreachable_patterns)]
fn _type_assertion(
_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>,
) {
fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
match _t {
alloy_sol_types::private::AssertTypeEq::<
<UnderlyingSolTuple as alloy_sol_types::SolType>::RustType,
@@ -539,9 +498,7 @@ function IS_SCRIPT() external view returns (bool);
type UnderlyingRustTuple<'a> = (bool,);
#[cfg(test)]
#[allow(dead_code, unreachable_patterns)]
fn _type_assertion(
_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>,
) {
fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
match _t {
alloy_sol_types::private::AssertTypeEq::<
<UnderlyingSolTuple as alloy_sol_types::SolType>::RustType,
@@ -566,14 +523,10 @@ function IS_SCRIPT() external view returns (bool);
#[automatically_derived]
impl alloy_sol_types::SolCall for IS_SCRIPTCall {
type Parameters<'a> = ();
type Token<'a> = <Self::Parameters<
'a,
> as alloy_sol_types::SolType>::Token<'a>;
type Token<'a> = <Self::Parameters<'a> as alloy_sol_types::SolType>::Token<'a>;
type Return = IS_SCRIPTReturn;
type ReturnTuple<'a> = (alloy::sol_types::sol_data::Bool,);
type ReturnToken<'a> = <Self::ReturnTuple<
'a,
> as alloy_sol_types::SolType>::Token<'a>;
type ReturnToken<'a> = <Self::ReturnTuple<'a> as alloy_sol_types::SolType>::Token<'a>;
const SIGNATURE: &'static str = "IS_SCRIPT()";
const SELECTOR: [u8; 4] = [248u8, 204u8, 191u8, 71u8];
#[inline]
@@ -591,17 +544,17 @@ function IS_SCRIPT() external view returns (bool);
data: &[u8],
validate: bool,
) -> alloy_sol_types::Result<Self::Return> {
<Self::ReturnTuple<
'_,
> as alloy_sol_types::SolType>::abi_decode_sequence(data, validate)
.map(Into::into)
<Self::ReturnTuple<'_> as alloy_sol_types::SolType>::abi_decode_sequence(
data, validate,
)
.map(Into::into)
}
}
};
/**Function with signature `activeNetworkConfig()` and selector `0xd7b65745`.
```solidity
function activeNetworkConfig() external view returns (address deployer);
```*/
```solidity
function activeNetworkConfig() external view returns (address deployer);
```*/
#[allow(non_camel_case_types, non_snake_case)]
#[derive(Clone)]
pub struct activeNetworkConfigCall {}
@@ -621,9 +574,7 @@ function activeNetworkConfig() external view returns (address deployer);
type UnderlyingRustTuple<'a> = ();
#[cfg(test)]
#[allow(dead_code, unreachable_patterns)]
fn _type_assertion(
_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>,
) {
fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
match _t {
alloy_sol_types::private::AssertTypeEq::<
<UnderlyingSolTuple as alloy_sol_types::SolType>::RustType,
@@ -632,16 +583,14 @@ function activeNetworkConfig() external view returns (address deployer);
}
#[automatically_derived]
#[doc(hidden)]
impl ::core::convert::From<activeNetworkConfigCall>
for UnderlyingRustTuple<'_> {
impl ::core::convert::From<activeNetworkConfigCall> for UnderlyingRustTuple<'_> {
fn from(value: activeNetworkConfigCall) -> Self {
()
}
}
#[automatically_derived]
#[doc(hidden)]
impl ::core::convert::From<UnderlyingRustTuple<'_>>
for activeNetworkConfigCall {
impl ::core::convert::From<UnderlyingRustTuple<'_>> for activeNetworkConfigCall {
fn from(tuple: UnderlyingRustTuple<'_>) -> Self {
Self {}
}
@@ -654,9 +603,7 @@ function activeNetworkConfig() external view returns (address deployer);
type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,);
#[cfg(test)]
#[allow(dead_code, unreachable_patterns)]
fn _type_assertion(
_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>,
) {
fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
match _t {
alloy_sol_types::private::AssertTypeEq::<
<UnderlyingSolTuple as alloy_sol_types::SolType>::RustType,
@@ -665,16 +612,14 @@ function activeNetworkConfig() external view returns (address deployer);
}
#[automatically_derived]
#[doc(hidden)]
impl ::core::convert::From<activeNetworkConfigReturn>
for UnderlyingRustTuple<'_> {
impl ::core::convert::From<activeNetworkConfigReturn> for UnderlyingRustTuple<'_> {
fn from(value: activeNetworkConfigReturn) -> Self {
(value.deployer,)
}
}
#[automatically_derived]
#[doc(hidden)]
impl ::core::convert::From<UnderlyingRustTuple<'_>>
for activeNetworkConfigReturn {
impl ::core::convert::From<UnderlyingRustTuple<'_>> for activeNetworkConfigReturn {
fn from(tuple: UnderlyingRustTuple<'_>) -> Self {
Self { deployer: tuple.0 }
}
@@ -683,14 +628,10 @@ function activeNetworkConfig() external view returns (address deployer);
#[automatically_derived]
impl alloy_sol_types::SolCall for activeNetworkConfigCall {
type Parameters<'a> = ();
type Token<'a> = <Self::Parameters<
'a,
> as alloy_sol_types::SolType>::Token<'a>;
type Token<'a> = <Self::Parameters<'a> as alloy_sol_types::SolType>::Token<'a>;
type Return = activeNetworkConfigReturn;
type ReturnTuple<'a> = (alloy::sol_types::sol_data::Address,);
type ReturnToken<'a> = <Self::ReturnTuple<
'a,
> as alloy_sol_types::SolType>::Token<'a>;
type ReturnToken<'a> = <Self::ReturnTuple<'a> as alloy_sol_types::SolType>::Token<'a>;
const SIGNATURE: &'static str = "activeNetworkConfig()";
const SELECTOR: [u8; 4] = [215u8, 182u8, 87u8, 69u8];
#[inline]
@@ -708,17 +649,17 @@ function activeNetworkConfig() external view returns (address deployer);
data: &[u8],
validate: bool,
) -> alloy_sol_types::Result<Self::Return> {
<Self::ReturnTuple<
'_,
> as alloy_sol_types::SolType>::abi_decode_sequence(data, validate)
.map(Into::into)
<Self::ReturnTuple<'_> as alloy_sol_types::SolType>::abi_decode_sequence(
data, validate,
)
.map(Into::into)
}
}
};
/**Function with signature `getOrCreateAnvilEthConfig()` and selector `0x12900da8`.
```solidity
function getOrCreateAnvilEthConfig() external view returns (NetworkConfig memory);
```*/
```solidity
function getOrCreateAnvilEthConfig() external view returns (NetworkConfig memory);
```*/
#[allow(non_camel_case_types, non_snake_case)]
#[derive(Clone)]
pub struct getOrCreateAnvilEthConfigCall {}
@@ -738,9 +679,7 @@ function getOrCreateAnvilEthConfig() external view returns (NetworkConfig memory
type UnderlyingRustTuple<'a> = ();
#[cfg(test)]
#[allow(dead_code, unreachable_patterns)]
fn _type_assertion(
_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>,
) {
fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
match _t {
alloy_sol_types::private::AssertTypeEq::<
<UnderlyingSolTuple as alloy_sol_types::SolType>::RustType,
@@ -749,16 +688,14 @@ function getOrCreateAnvilEthConfig() external view returns (NetworkConfig memory
}
#[automatically_derived]
#[doc(hidden)]
impl ::core::convert::From<getOrCreateAnvilEthConfigCall>
for UnderlyingRustTuple<'_> {
impl ::core::convert::From<getOrCreateAnvilEthConfigCall> for UnderlyingRustTuple<'_> {
fn from(value: getOrCreateAnvilEthConfigCall) -> Self {
()
}
}
#[automatically_derived]
#[doc(hidden)]
impl ::core::convert::From<UnderlyingRustTuple<'_>>
for getOrCreateAnvilEthConfigCall {
impl ::core::convert::From<UnderlyingRustTuple<'_>> for getOrCreateAnvilEthConfigCall {
fn from(tuple: UnderlyingRustTuple<'_>) -> Self {
Self {}
}
@@ -768,14 +705,11 @@ function getOrCreateAnvilEthConfig() external view returns (NetworkConfig memory
#[doc(hidden)]
type UnderlyingSolTuple<'a> = (NetworkConfig,);
#[doc(hidden)]
type UnderlyingRustTuple<'a> = (
<NetworkConfig as alloy::sol_types::SolType>::RustType,
);
type UnderlyingRustTuple<'a> =
(<NetworkConfig as alloy::sol_types::SolType>::RustType,);
#[cfg(test)]
#[allow(dead_code, unreachable_patterns)]
fn _type_assertion(
_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>,
) {
fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
match _t {
alloy_sol_types::private::AssertTypeEq::<
<UnderlyingSolTuple as alloy_sol_types::SolType>::RustType,
@@ -784,16 +718,14 @@ function getOrCreateAnvilEthConfig() external view returns (NetworkConfig memory
}
#[automatically_derived]
#[doc(hidden)]
impl ::core::convert::From<getOrCreateAnvilEthConfigReturn>
for UnderlyingRustTuple<'_> {
impl ::core::convert::From<getOrCreateAnvilEthConfigReturn> for UnderlyingRustTuple<'_> {
fn from(value: getOrCreateAnvilEthConfigReturn) -> Self {
(value._0,)
}
}
#[automatically_derived]
#[doc(hidden)]
impl ::core::convert::From<UnderlyingRustTuple<'_>>
for getOrCreateAnvilEthConfigReturn {
impl ::core::convert::From<UnderlyingRustTuple<'_>> for getOrCreateAnvilEthConfigReturn {
fn from(tuple: UnderlyingRustTuple<'_>) -> Self {
Self { _0: tuple.0 }
}
@@ -802,14 +734,10 @@ function getOrCreateAnvilEthConfig() external view returns (NetworkConfig memory
#[automatically_derived]
impl alloy_sol_types::SolCall for getOrCreateAnvilEthConfigCall {
type Parameters<'a> = ();
type Token<'a> = <Self::Parameters<
'a,
> as alloy_sol_types::SolType>::Token<'a>;
type Token<'a> = <Self::Parameters<'a> as alloy_sol_types::SolType>::Token<'a>;
type Return = getOrCreateAnvilEthConfigReturn;
type ReturnTuple<'a> = (NetworkConfig,);
type ReturnToken<'a> = <Self::ReturnTuple<
'a,
> as alloy_sol_types::SolType>::Token<'a>;
type ReturnToken<'a> = <Self::ReturnTuple<'a> as alloy_sol_types::SolType>::Token<'a>;
const SIGNATURE: &'static str = "getOrCreateAnvilEthConfig()";
const SELECTOR: [u8; 4] = [18u8, 144u8, 13u8, 168u8];
#[inline]
@@ -827,17 +755,17 @@ function getOrCreateAnvilEthConfig() external view returns (NetworkConfig memory
data: &[u8],
validate: bool,
) -> alloy_sol_types::Result<Self::Return> {
<Self::ReturnTuple<
'_,
> as alloy_sol_types::SolType>::abi_decode_sequence(data, validate)
.map(Into::into)
<Self::ReturnTuple<'_> as alloy_sol_types::SolType>::abi_decode_sequence(
data, validate,
)
.map(Into::into)
}
}
};
/**Function with signature `test()` and selector `0xf8a8fd6d`.
```solidity
function test() external;
```*/
```solidity
function test() external;
```*/
#[allow(non_camel_case_types, non_snake_case)]
#[derive(Clone)]
pub struct testCall {}
@@ -855,9 +783,7 @@ function test() external;
type UnderlyingRustTuple<'a> = ();
#[cfg(test)]
#[allow(dead_code, unreachable_patterns)]
fn _type_assertion(
_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>,
) {
fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
match _t {
alloy_sol_types::private::AssertTypeEq::<
<UnderlyingSolTuple as alloy_sol_types::SolType>::RustType,
@@ -886,9 +812,7 @@ function test() external;
type UnderlyingRustTuple<'a> = ();
#[cfg(test)]
#[allow(dead_code, unreachable_patterns)]
fn _type_assertion(
_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>,
) {
fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
match _t {
alloy_sol_types::private::AssertTypeEq::<
<UnderlyingSolTuple as alloy_sol_types::SolType>::RustType,
@@ -913,14 +837,10 @@ function test() external;
#[automatically_derived]
impl alloy_sol_types::SolCall for testCall {
type Parameters<'a> = ();
type Token<'a> = <Self::Parameters<
'a,
> as alloy_sol_types::SolType>::Token<'a>;
type Token<'a> = <Self::Parameters<'a> as alloy_sol_types::SolType>::Token<'a>;
type Return = testReturn;
type ReturnTuple<'a> = ();
type ReturnToken<'a> = <Self::ReturnTuple<
'a,
> as alloy_sol_types::SolType>::Token<'a>;
type ReturnToken<'a> = <Self::ReturnTuple<'a> as alloy_sol_types::SolType>::Token<'a>;
const SIGNATURE: &'static str = "test()";
const SELECTOR: [u8; 4] = [248u8, 168u8, 253u8, 109u8];
#[inline]
@@ -938,10 +858,10 @@ function test() external;
data: &[u8],
validate: bool,
) -> alloy_sol_types::Result<Self::Return> {
<Self::ReturnTuple<
'_,
> as alloy_sol_types::SolType>::abi_decode_sequence(data, validate)
.map(Into::into)
<Self::ReturnTuple<'_> as alloy_sol_types::SolType>::abi_decode_sequence(
data, validate,
)
.map(Into::into)
}
}
};
@@ -975,9 +895,7 @@ function test() external;
#[inline]
fn selector(&self) -> [u8; 4] {
match self {
Self::IS_SCRIPT(_) => {
<IS_SCRIPTCall as alloy_sol_types::SolCall>::SELECTOR
}
Self::IS_SCRIPT(_) => <IS_SCRIPTCall as alloy_sol_types::SolCall>::SELECTOR,
Self::activeNetworkConfig(_) => {
<activeNetworkConfigCall as alloy_sol_types::SolCall>::SELECTOR
}
@@ -1005,17 +923,17 @@ function test() external;
static DECODE_SHIMS: &[fn(
&[u8],
bool,
) -> alloy_sol_types::Result<DeploymentConfigCalls>] = &[
)
-> alloy_sol_types::Result<DeploymentConfigCalls>] = &[
{
fn getOrCreateAnvilEthConfig(
data: &[u8],
validate: bool,
) -> alloy_sol_types::Result<DeploymentConfigCalls> {
<getOrCreateAnvilEthConfigCall as alloy_sol_types::SolCall>::abi_decode_raw(
data,
validate,
)
.map(DeploymentConfigCalls::getOrCreateAnvilEthConfig)
data, validate,
)
.map(DeploymentConfigCalls::getOrCreateAnvilEthConfig)
}
getOrCreateAnvilEthConfig
},
@@ -1025,10 +943,9 @@ function test() external;
validate: bool,
) -> alloy_sol_types::Result<DeploymentConfigCalls> {
<activeNetworkConfigCall as alloy_sol_types::SolCall>::abi_decode_raw(
data,
validate,
)
.map(DeploymentConfigCalls::activeNetworkConfig)
data, validate,
)
.map(DeploymentConfigCalls::activeNetworkConfig)
}
activeNetworkConfig
},
@@ -1037,10 +954,7 @@ function test() external;
data: &[u8],
validate: bool,
) -> alloy_sol_types::Result<DeploymentConfigCalls> {
<testCall as alloy_sol_types::SolCall>::abi_decode_raw(
data,
validate,
)
<testCall as alloy_sol_types::SolCall>::abi_decode_raw(data, validate)
.map(DeploymentConfigCalls::test)
}
test
@@ -1050,22 +964,17 @@ function test() external;
data: &[u8],
validate: bool,
) -> alloy_sol_types::Result<DeploymentConfigCalls> {
<IS_SCRIPTCall as alloy_sol_types::SolCall>::abi_decode_raw(
data,
validate,
)
<IS_SCRIPTCall as alloy_sol_types::SolCall>::abi_decode_raw(data, validate)
.map(DeploymentConfigCalls::IS_SCRIPT)
}
IS_SCRIPT
},
];
let Ok(idx) = Self::SELECTORS.binary_search(&selector) else {
return Err(
alloy_sol_types::Error::unknown_selector(
<Self as alloy_sol_types::SolInterface>::NAME,
selector,
),
);
return Err(alloy_sol_types::Error::unknown_selector(
<Self as alloy_sol_types::SolInterface>::NAME,
selector,
));
};
(unsafe { DECODE_SHIMS.get_unchecked(idx) })(data, validate)
}
@@ -1076,9 +985,7 @@ function test() external;
<IS_SCRIPTCall as alloy_sol_types::SolCall>::abi_encoded_size(inner)
}
Self::activeNetworkConfig(inner) => {
<activeNetworkConfigCall as alloy_sol_types::SolCall>::abi_encoded_size(
inner,
)
<activeNetworkConfigCall as alloy_sol_types::SolCall>::abi_encoded_size(inner)
}
Self::getOrCreateAnvilEthConfig(inner) => {
<getOrCreateAnvilEthConfigCall as alloy_sol_types::SolCall>::abi_encoded_size(
@@ -1094,21 +1001,16 @@ function test() external;
fn abi_encode_raw(&self, out: &mut alloy_sol_types::private::Vec<u8>) {
match self {
Self::IS_SCRIPT(inner) => {
<IS_SCRIPTCall as alloy_sol_types::SolCall>::abi_encode_raw(
inner,
out,
)
<IS_SCRIPTCall as alloy_sol_types::SolCall>::abi_encode_raw(inner, out)
}
Self::activeNetworkConfig(inner) => {
<activeNetworkConfigCall as alloy_sol_types::SolCall>::abi_encode_raw(
inner,
out,
inner, out,
)
}
Self::getOrCreateAnvilEthConfig(inner) => {
<getOrCreateAnvilEthConfigCall as alloy_sol_types::SolCall>::abi_encode_raw(
inner,
out,
inner, out,
)
}
Self::test(inner) => {
@@ -1130,10 +1032,8 @@ function test() external;
/// No guarantees are made about the order of the selectors.
///
/// Prefer using `SolInterface` methods instead.
pub const SELECTORS: &'static [[u8; 4usize]] = &[
[11u8, 19u8, 219u8, 255u8],
[128u8, 88u8, 91u8, 68u8],
];
pub const SELECTORS: &'static [[u8; 4usize]] =
&[[11u8, 19u8, 219u8, 255u8], [128u8, 88u8, 91u8, 68u8]];
}
#[automatically_derived]
impl alloy_sol_types::SolInterface for DeploymentConfigErrors {
@@ -1169,7 +1069,8 @@ function test() external;
static DECODE_SHIMS: &[fn(
&[u8],
bool,
) -> alloy_sol_types::Result<DeploymentConfigErrors>] = &[
)
-> alloy_sol_types::Result<DeploymentConfigErrors>] = &[
{
fn DeploymentConfig_NoConfigForChain(
data: &[u8],
@@ -1202,12 +1103,10 @@ function test() external;
},
];
let Ok(idx) = Self::SELECTORS.binary_search(&selector) else {
return Err(
alloy_sol_types::Error::unknown_selector(
<Self as alloy_sol_types::SolInterface>::NAME,
selector,
),
);
return Err(alloy_sol_types::Error::unknown_selector(
<Self as alloy_sol_types::SolInterface>::NAME,
selector,
));
};
(unsafe { DECODE_SHIMS.get_unchecked(idx) })(data, validate)
}
@@ -1247,7 +1146,7 @@ function test() external;
use alloy::contract as alloy_contract;
/**Creates a new wrapper around an on-chain [`DeploymentConfig`](self) contract instance.
See the [wrapper's documentation](`DeploymentConfigInstance`) for more details.*/
See the [wrapper's documentation](`DeploymentConfigInstance`) for more details.*/
#[inline]
pub const fn new<
T: alloy_contract::private::Transport + ::core::clone::Clone,
@@ -1261,9 +1160,9 @@ See the [wrapper's documentation](`DeploymentConfigInstance`) for more details.*
}
/**Deploys this contract using the given `provider` and constructor arguments, if any.
Returns a new instance of the contract, if the deployment was successful.
Returns a new instance of the contract, if the deployment was successful.
For more fine-grained control over the deployment process, use [`deploy_builder`] instead.*/
For more fine-grained control over the deployment process, use [`deploy_builder`] instead.*/
#[inline]
pub fn deploy<
T: alloy_contract::private::Transport + ::core::clone::Clone,
@@ -1272,16 +1171,15 @@ For more fine-grained control over the deployment process, use [`deploy_builder`
>(
provider: P,
_broadcaster: alloy::sol_types::private::Address,
) -> impl ::core::future::Future<
Output = alloy_contract::Result<DeploymentConfigInstance<T, P, N>>,
> {
) -> impl ::core::future::Future<Output = alloy_contract::Result<DeploymentConfigInstance<T, P, N>>>
{
DeploymentConfigInstance::<T, P, N>::deploy(provider, _broadcaster)
}
/**Creates a `RawCallBuilder` for deploying this contract using the given `provider`
and constructor arguments, if any.
and constructor arguments, if any.
This is a simple wrapper around creating a `RawCallBuilder` with the data set to
the bytecode concatenated with the constructor's ABI-encoded arguments.*/
This is a simple wrapper around creating a `RawCallBuilder` with the data set to
the bytecode concatenated with the constructor's ABI-encoded arguments.*/
#[inline]
pub fn deploy_builder<
T: alloy_contract::private::Transport + ::core::clone::Clone,
@@ -1295,15 +1193,15 @@ the bytecode concatenated with the constructor's ABI-encoded arguments.*/
}
/**A [`DeploymentConfig`](self) instance.
Contains type-safe methods for interacting with an on-chain instance of the
[`DeploymentConfig`](self) contract located at a given `address`, using a given
provider `P`.
Contains type-safe methods for interacting with an on-chain instance of the
[`DeploymentConfig`](self) contract located at a given `address`, using a given
provider `P`.
If the contract bytecode is available (see the [`sol!`](alloy_sol_types::sol!)
documentation on how to provide it), the `deploy` and `deploy_builder` methods can
be used to deploy a new instance of the contract.
If the contract bytecode is available (see the [`sol!`](alloy_sol_types::sol!)
documentation on how to provide it), the `deploy` and `deploy_builder` methods can
be used to deploy a new instance of the contract.
See the [module-level documentation](self) for all the available methods.*/
See the [module-level documentation](self) for all the available methods.*/
#[derive(Clone)]
pub struct DeploymentConfigInstance<T, P, N = alloy_contract::private::Ethereum> {
address: alloy_sol_types::private::Address,
@@ -1314,24 +1212,24 @@ See the [module-level documentation](self) for all the available methods.*/
impl<T, P, N> ::core::fmt::Debug for DeploymentConfigInstance<T, P, N> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_tuple("DeploymentConfigInstance").field(&self.address).finish()
f.debug_tuple("DeploymentConfigInstance")
.field(&self.address)
.finish()
}
}
/// Instantiation and getters/setters.
#[automatically_derived]
impl<
T: alloy_contract::private::Transport + ::core::clone::Clone,
P: alloy_contract::private::Provider<T, N>,
N: alloy_contract::private::Network,
> DeploymentConfigInstance<T, P, N> {
T: alloy_contract::private::Transport + ::core::clone::Clone,
P: alloy_contract::private::Provider<T, N>,
N: alloy_contract::private::Network,
> DeploymentConfigInstance<T, P, N>
{
/**Creates a new wrapper around an on-chain [`DeploymentConfig`](self) contract instance.
See the [wrapper's documentation](`DeploymentConfigInstance`) for more details.*/
See the [wrapper's documentation](`DeploymentConfigInstance`) for more details.*/
#[inline]
pub const fn new(
address: alloy_sol_types::private::Address,
provider: P,
) -> Self {
pub const fn new(address: alloy_sol_types::private::Address, provider: P) -> Self {
Self {
address,
provider,
@@ -1340,9 +1238,9 @@ See the [wrapper's documentation](`DeploymentConfigInstance`) for more details.*
}
/**Deploys this contract using the given `provider` and constructor arguments, if any.
Returns a new instance of the contract, if the deployment was successful.
Returns a new instance of the contract, if the deployment was successful.
For more fine-grained control over the deployment process, use [`deploy_builder`] instead.*/
For more fine-grained control over the deployment process, use [`deploy_builder`] instead.*/
#[inline]
pub async fn deploy(
provider: P,
@@ -1353,10 +1251,10 @@ For more fine-grained control over the deployment process, use [`deploy_builder`
Ok(Self::new(contract_address, call_builder.provider))
}
/**Creates a `RawCallBuilder` for deploying this contract using the given `provider`
and constructor arguments, if any.
and constructor arguments, if any.
This is a simple wrapper around creating a `RawCallBuilder` with the data set to
the bytecode concatenated with the constructor's ABI-encoded arguments.*/
This is a simple wrapper around creating a `RawCallBuilder` with the data set to
the bytecode concatenated with the constructor's ABI-encoded arguments.*/
#[inline]
pub fn deploy_builder(
provider: P,
@@ -1366,12 +1264,11 @@ the bytecode concatenated with the constructor's ABI-encoded arguments.*/
provider,
[
&BYTECODE[..],
&alloy_sol_types::SolConstructor::abi_encode(
&constructorCall { _broadcaster },
)[..],
&alloy_sol_types::SolConstructor::abi_encode(&constructorCall { _broadcaster })
[..],
]
.concat()
.into(),
.concat()
.into(),
)
}
/// Returns a reference to the address.
@@ -1409,10 +1306,11 @@ the bytecode concatenated with the constructor's ABI-encoded arguments.*/
/// Function calls.
#[automatically_derived]
impl<
T: alloy_contract::private::Transport + ::core::clone::Clone,
P: alloy_contract::private::Provider<T, N>,
N: alloy_contract::private::Network,
> DeploymentConfigInstance<T, P, N> {
T: alloy_contract::private::Transport + ::core::clone::Clone,
P: alloy_contract::private::Provider<T, N>,
N: alloy_contract::private::Network,
> DeploymentConfigInstance<T, P, N>
{
/// Creates a new call builder using this contract instance's provider and address.
///
/// Note that the call can be any function call, not just those defined in this
@@ -1424,9 +1322,7 @@ the bytecode concatenated with the constructor's ABI-encoded arguments.*/
alloy_contract::SolCallBuilder::new_sol(&self.provider, &self.address, call)
}
///Creates a new call builder for the [`IS_SCRIPT`] function.
pub fn IS_SCRIPT(
&self,
) -> alloy_contract::SolCallBuilder<T, &P, IS_SCRIPTCall, N> {
pub fn IS_SCRIPT(&self) -> alloy_contract::SolCallBuilder<T, &P, IS_SCRIPTCall, N> {
self.call_builder(&IS_SCRIPTCall {})
}
///Creates a new call builder for the [`activeNetworkConfig`] function.
@@ -1449,10 +1345,11 @@ the bytecode concatenated with the constructor's ABI-encoded arguments.*/
/// Event filters.
#[automatically_derived]
impl<
T: alloy_contract::private::Transport + ::core::clone::Clone,
P: alloy_contract::private::Provider<T, N>,
N: alloy_contract::private::Network,
> DeploymentConfigInstance<T, P, N> {
T: alloy_contract::private::Transport + ::core::clone::Clone,
P: alloy_contract::private::Provider<T, N>,
N: alloy_contract::private::Network,
> DeploymentConfigInstance<T, P, N>
{
/// Creates a new event filter using this contract instance's provider and address.
///
/// Note that the type can be any event, not just those defined in this contract.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -15,9 +15,11 @@ tokio = "=1.38.0"
openmls = { version = "=0.5.0", features = ["test-utils"] }
rand = { version = "^0.8" }
anyhow = "=1.0.71"
anyhow = "=1.0.81"
thiserror = "=1.0.61"
tls_codec = "=0.3.0"
serde_json = "=1.0"
serde = "=1.0.204"
sc_key_store = { path = "../sc_key_store" }

View File

@@ -4,36 +4,44 @@ use fred::{
prelude::*,
types::Message,
};
use serde::{Deserialize, Serialize};
use tokio::sync::broadcast::{error::RecvError, Receiver};
use openmls::{
framing::{MlsMessageIn, MlsMessageOut},
prelude::{TlsDeserializeTrait, TlsSerializeTrait},
};
use openmls::{framing::MlsMessageOut, prelude::TlsSerializeTrait};
// use waku_bindings::*;
pub struct RClient {
group_id: String,
client: RedisClient,
sub_client: SubscriberClient,
broadcaster: Receiver<Message>,
// broadcaster: Receiver<Message>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SenderStruct {
pub sender: String,
pub msg: Vec<u8>,
}
impl RClient {
pub async fn new_for_group(group_id: String) -> Result<Self, DeliveryServiceError> {
pub async fn new_for_group(
group_id: String,
) -> Result<(Self, Receiver<Message>), DeliveryServiceError> {
let redis_client = RedisClient::default();
let subscriber: SubscriberClient =
Builder::default_centralized().build_subscriber_client()?;
redis_client.init().await?;
subscriber.init().await?;
subscriber.subscribe(group_id.clone()).await?;
Ok(RClient {
group_id,
client: redis_client,
sub_client: subscriber.clone(),
broadcaster: subscriber.message_rx(),
})
Ok((
RClient {
group_id,
client: redis_client,
sub_client: subscriber.clone(),
// broadcaster: subscriber.message_rx(),
},
subscriber.message_rx(),
))
}
pub async fn remove_from_group(&mut self) -> Result<(), DeliveryServiceError> {
@@ -43,30 +51,31 @@ impl RClient {
Ok(())
}
pub async fn msg_send(&mut self, msg: MlsMessageOut) -> Result<(), DeliveryServiceError> {
pub async fn msg_send(
&mut self,
msg: MlsMessageOut,
sender: String,
) -> Result<(), DeliveryServiceError> {
let buf = msg.tls_serialize_detached()?;
let json_value = SenderStruct { sender, msg: buf };
let bytes = serde_json::to_vec(&json_value)?;
self.client
.publish(self.group_id.clone(), buf.as_slice())
.publish(self.group_id.clone(), bytes.as_slice())
.await?;
Ok(())
}
pub async fn msg_recv(&mut self) -> Result<MlsMessageIn, DeliveryServiceError> {
// check only one message
let msg = self.broadcaster.recv().await?;
let bytes: Vec<u8> = msg.value.convert()?;
let res = MlsMessageIn::tls_deserialize_bytes(bytes)?;
Ok(res)
}
}
#[derive(Debug, thiserror::Error)]
pub enum DeliveryServiceError {
#[error("Json error: {0}")]
JsonError(#[from] serde_json::Error),
#[error("Redis error: {0}")]
RedisError(#[from] RedisError),
#[error("Tokio error: {0}")]
TokioRecieveError(#[from] RecvError),
TokioReceiveError(#[from] RecvError),
#[error("Serialization problem: {0}")]
TlsError(#[from] tls_codec::Error),
#[error("Unknown error: {0}")]

View File

@@ -9,11 +9,12 @@ edition = "2021"
foundry-contracts.workspace = true
openmls = { version = "=0.5.0", features = ["test-utils"] }
openmls_basic_credential = "=0.2.0"
tls_codec = "=0.3.0"
thiserror = "=1.0.61"
anyhow = "=1.0.71"
anyhow = "=1.0.81"
tls_codec = "=0.3.0"
hex = "0.4.3"
url = "2.5.2"
eyre = "=0.6"
@@ -27,8 +28,5 @@ alloy = { git = "https://github.com/alloy-rs/alloy", features = [
"transport-http",
"k256",
] }
async-trait = "0.1.80"
p256 = "0.13.2"
mls_crypto = { path = "../mls_crypto" }
hex = "0.4.3"

View File

@@ -1,99 +0,0 @@
use openmls::prelude::KeyPackage;
use std::collections::HashMap;
use mls_crypto::openmls_provider::MlsCryptoProvider;
use crate::{KeyStoreError, SCKeyStoreService, UserInfo, UserKeyPackages};
/// Public Key Storage
/// This is a tuple struct holding a vector of `(Vec<u8>, UserInfo)` tuples,
/// where the first value is the Ethereum wallet address of a user
/// and the second value is the corresponding user information.
#[derive(Debug, Default)]
pub struct PublicKeyStorage {
storage: HashMap<Vec<u8>, UserInfo>,
}
impl From<UserKeyPackages> for UserInfo {
fn from(mut key_packages: UserKeyPackages) -> Self {
let key_package: KeyPackage = key_packages.0[0].1.clone();
let id = key_package.leaf_node().credential().identity();
let drain = key_packages.0.drain(..);
Self {
id: id.into(),
key_packages: UserKeyPackages(drain.collect::<Vec<(Vec<u8>, KeyPackage)>>()),
sign_pk: vec![0; 32],
}
}
}
impl SCKeyStoreService for &mut PublicKeyStorage {
async fn does_user_exist(&self, id: &[u8]) -> Result<bool, KeyStoreError> {
Ok(self.storage.contains_key(id))
}
async fn add_user(
&mut self,
ukp: UserKeyPackages,
sign_pk: &[u8],
) -> Result<(), KeyStoreError> {
if ukp.0.is_empty() {
return Err(KeyStoreError::InvalidUserDataError(
"no key packages".to_string(),
));
}
let mut new_user_info: UserInfo = ukp.into();
new_user_info.sign_pk.clone_from_slice(sign_pk);
if self.storage.contains_key(&new_user_info.id) {
return Err(KeyStoreError::InvalidUserDataError(
"already register".to_string(),
));
}
let res = self.storage.insert(new_user_info.id.clone(), new_user_info);
assert!(res.is_none());
Ok(())
}
async fn add_user_kp(&mut self, id: &[u8], ukp: UserKeyPackages) -> Result<(), KeyStoreError> {
let user = match self.storage.get_mut(id) {
Some(u) => u,
None => return Err(KeyStoreError::UnknownUserError),
};
ukp.0
.into_iter()
.for_each(|value| user.key_packages.0.push(value));
Ok(())
}
async fn get_user(
&self,
id: &[u8],
_crypto: &MlsCryptoProvider,
) -> Result<UserInfo, KeyStoreError> {
match self.storage.get(id) {
Some(u) => Ok(u.to_owned()),
None => Err(KeyStoreError::UnknownUserError),
}
}
async fn get_avaliable_user_kp(
&mut self,
id: &[u8],
_crypto: &MlsCryptoProvider,
) -> Result<KeyPackage, KeyStoreError> {
let user = match self.storage.get_mut(id) {
Some(u) => u,
None => return Err(KeyStoreError::UnknownUserError),
};
match user.key_packages.0.pop() {
Some(c) => Ok(c.1),
None => Err(KeyStoreError::InvalidUserDataError(
"No more keypackage available".to_string(),
)),
}
}
}

View File

@@ -191,7 +191,7 @@ mod test {
use openmls::prelude::*;
use openmls_basic_credential::SignatureKeyPair;
use mls_crypto::openmls_provider::MlsCryptoProvider;
use mls_crypto::openmls_provider::{MlsCryptoProvider, CIPHERSUITE};
use crate::{sc_ks::*, UserKeyPackages};
@@ -199,7 +199,7 @@ mod test {
address: Address,
crypto: &MlsCryptoProvider,
) -> (UserKeyPackages, SignatureKeyPair) {
let ciphersuite = Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256;
let ciphersuite = CIPHERSUITE;
let signature_keys = SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap();
let credential = Credential::new(address.to_vec(), CredentialType::Basic).unwrap();

297
src/cli.rs Normal file
View File

@@ -0,0 +1,297 @@
use alloy::{
hex::FromHexError,
network::EthereumWallet,
primitives::Address,
signers::local::{LocalSignerError, PrivateKeySigner},
};
use clap::{arg, command, Parser, Subcommand};
use crossterm::{
event::{self, Event, KeyCode},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use fred::error::RedisError;
use ratatui::{
backend::CrosstermBackend,
layout::{Constraint, Direction, Layout},
style::{Color, Modifier, Style},
text::Text,
widgets::{Block, Borders, List, ListItem, Paragraph, Wrap},
Frame, Terminal,
};
use std::{
io,
io::{Read, Write},
str::FromStr,
string::FromUtf8Error,
sync::Arc,
};
use tokio::{
sync::mpsc::error::SendError,
sync::mpsc::{Receiver, Sender},
sync::Mutex,
task::JoinError,
};
use tokio_util::sync::CancellationToken;
use url::Url;
use crate::user::UserError;
use ds::ds::DeliveryServiceError;
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
pub struct Args {
/// User private key that correspond to Ethereum wallet
#[arg(short = 'K', long)]
user_priv_key: String,
/// Rpc url
#[arg(short = 'U', long,
default_value_t = Url::from_str("http://localhost:8545").unwrap())]
pub storage_url: Url,
/// Storage contract address
#[arg(short = 'S', long)]
pub storage_addr: String,
}
pub enum Msg {
Input(Message),
Refresh(String),
Exit,
}
#[derive(Clone)]
pub enum Message {
Incoming(String, String, String),
Mine(String, String, String),
System(String),
Error(String),
}
pub fn get_user_data(args: &Args) -> Result<(Address, EthereumWallet, Address), CliError> {
let signer = PrivateKeySigner::from_str(&args.user_priv_key)?;
let user_address = signer.address();
let wallet = EthereumWallet::from(signer);
let storage_address = Address::from_str(&args.storage_addr)?;
Ok((user_address, wallet, storage_address))
}
#[derive(Debug, Parser)]
#[command(multicall = true)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Debug, Subcommand, Clone)]
pub enum Commands {
CreateGroup {
group_name: String,
},
Invite {
group_name: String,
user_wallet: String,
},
JoinGroup {
group_name: String,
},
SendMessage {
group_name: String,
msg: Vec<String>,
},
// RemoveUser { user_wallet: String },
Exit,
}
pub fn readline() -> Result<String, CliError> {
write!(std::io::stdout(), "$ ")?;
std::io::stdout().flush()?;
let mut buffer = String::new();
std::io::stdin().read_to_string(&mut buffer)?;
Ok(buffer)
}
pub async fn event_handler(
messages_tx: Sender<Msg>,
cli_tx: Sender<Commands>,
token: CancellationToken,
) -> Result<(), CliError> {
let mut input = String::new();
loop {
if let Event::Key(key) = tokio::task::spawn_blocking(event::read).await?? {
match key.code {
KeyCode::Char(c) => {
input.push(c);
}
KeyCode::Backspace => {
input.pop();
}
KeyCode::Enter => {
let line: String = std::mem::take(&mut input);
let args = shlex::split(&line).ok_or(CliError::SplitLineError)?;
let cli = Cli::try_parse_from(args);
if cli.is_err() {
messages_tx
.send(Msg::Input(Message::System("Unknown command".to_string())))
.await?;
continue;
}
cli_tx.send(cli.unwrap().command).await?;
}
KeyCode::Esc => {
messages_tx.send(Msg::Exit).await?;
token.cancel();
break;
}
_ => {}
}
messages_tx.send(Msg::Refresh(input.clone())).await?;
}
}
Ok::<_, CliError>(())
}
pub fn ui(f: &mut Frame, messages: &[Message], input: &str) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(1), Constraint::Length(3)].as_ref())
.split(f.size());
let message_items: Vec<ListItem> = messages
.iter()
.map(|message| {
let (content, style) = match message {
Message::Incoming(group, from, msg) => (
format!("[0x{}]@{}: {}", from, group, msg),
Style::default().fg(Color::LightGreen),
),
Message::Mine(group, from, msg) => (
format!("[0x{}]@{}: {}", from, group, msg),
Style::default()
.fg(Color::LightGreen)
.add_modifier(Modifier::BOLD),
),
Message::System(msg) => (format!("[System]: {}", msg), Style::default()),
Message::Error(msg) => (msg.clone(), Style::default().fg(Color::LightRed)),
};
ListItem::new(content).style(style)
})
.collect();
let messages_history = List::new(message_items).block(
Block::default()
.borders(Borders::ALL)
.title("messages history"),
);
let input_line = Paragraph::new(Text::raw(input))
.style(
Style::default()
.fg(Color::Yellow)
.add_modifier(Modifier::BOLD),
)
.block(Block::default().borders(Borders::ALL).title("input line"))
.wrap(Wrap { trim: false });
f.render_widget(messages_history, chunks[0]);
f.render_widget(input_line, chunks[1]);
}
pub async fn terminal_handler(
mut messages_rx: Receiver<Msg>,
token: CancellationToken,
) -> Result<(), CliError> {
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout);
let terminal = Arc::new(Mutex::new(Terminal::new(backend)?));
let messages = Arc::new(Mutex::new(vec![]));
let input = Arc::new(Mutex::new(String::new()));
let messages_clone = Arc::clone(&messages);
let input_clone = Arc::clone(&input);
while let Some(msg) = messages_rx.recv().await {
match msg {
Msg::Input(m) => {
let mut messages = messages_clone.lock().await;
messages.push(m);
if messages.len() == 100 {
messages.remove(0);
}
}
Msg::Refresh(i) => {
let mut input = input_clone.lock().await;
*input = i;
}
Msg::Exit => {
token.cancel();
break;
}
};
let messages = Arc::clone(&messages_clone);
let input = Arc::clone(&input_clone);
let terminal = Arc::clone(&terminal);
tokio::task::spawn_blocking(move || {
let messages = messages.blocking_lock();
let input = input.blocking_lock();
terminal
.blocking_lock()
.draw(|f| ui(f, &messages, &input))
.unwrap();
})
.await?;
}
// Restore terminal
disable_raw_mode()?;
let mut terminal_lock = terminal.lock().await;
execute!(terminal_lock.backend_mut(), LeaveAlternateScreen)?;
terminal_lock.show_cursor()?;
Ok(())
}
#[derive(Debug, thiserror::Error)]
pub enum CliError {
#[error("Can't split the line")]
SplitLineError,
#[error("Unknown message type")]
UnknownMsgError,
#[error(transparent)]
UserError(#[from] UserError),
#[error(transparent)]
DeliveryServiceError(#[from] DeliveryServiceError),
#[error("Unable to parce the address: {0}")]
AlloyFromHexError(#[from] FromHexError),
#[error("Unable to parce the signer: {0}")]
AlloyParceSignerError(#[from] LocalSignerError),
#[error("Problem from std::io library: {0}")]
IoError(#[from] std::io::Error),
#[error("Parse String UTF8 error: {0}")]
ParseUTF8Error(#[from] FromUtf8Error),
#[error("Parse String error: {0}")]
StringError(#[from] core::convert::Infallible),
#[error("Can't send control message into channel: {0}")]
SendMsgError(#[from] SendError<Msg>),
#[error("Can't send bytes into channel: {0}")]
SendVecError(#[from] SendError<Vec<u8>>),
#[error("Can't send command into channel: {0}")]
SendCommandError(#[from] SendError<Commands>),
#[error(transparent)]
ClapError(#[from] clap::error::Error),
#[error("Redis error: {0}")]
RedisError(#[from] RedisError),
#[error("Failed from tokio join: {0}")]
TokioJoinError(#[from] JoinError),
#[error("Unknown error: {0}")]
AnyHowError(anyhow::Error),
}

View File

@@ -1,3 +1,5 @@
use std::fmt::Display;
#[derive(Default, Debug)]
pub struct Conversation {
messages: Vec<ConversationMessage>,
@@ -5,10 +7,21 @@ pub struct Conversation {
#[derive(Clone, Debug)]
pub struct ConversationMessage {
pub group: String,
pub author: String,
pub message: String,
}
impl Display for ConversationMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(
f,
"Group: {:#?}\nAuthor: {:#?}\nMessage: {:#?}",
self.group, self.author, self.message
)
}
}
impl Conversation {
/// Add a message string to the conversation list.
pub fn add(&mut self, conversation_message: ConversationMessage) {
@@ -29,7 +42,11 @@ impl Conversation {
}
impl ConversationMessage {
pub fn new(message: String, author: String) -> Self {
Self { author, message }
pub fn new(group: String, author: String, message: String) -> Self {
Self {
group,
author,
message,
}
}
}

View File

@@ -1,8 +1,7 @@
use hex;
use std::collections::HashMap;
use openmls::credentials::CredentialWithKey;
use openmls::key_packages::*;
use openmls::prelude::*;
use openmls::{credentials::CredentialWithKey, key_packages::*, prelude::*};
use openmls_basic_credential::SignatureKeyPair;
use openmls_rust_crypto::MemoryKeyStoreError;
use openmls_traits::types::Ciphersuite;
@@ -69,13 +68,15 @@ impl Identity {
pub fn identity(&self) -> Vec<u8> {
self.credential_with_key.credential.identity().to_vec()
}
pub fn signature_pub_key(&self) -> Vec<u8> {
self.signer.public().to_vec()
}
}
impl ToString for Identity {
fn to_string(&self) -> String {
std::str::from_utf8(self.credential_with_key.credential.identity())
.unwrap()
.to_string()
hex::encode(self.credential_with_key.credential.identity())
}
}

View File

@@ -1,3 +1,4 @@
pub mod cli;
pub mod conversation;
pub mod identity;
pub mod user;

View File

@@ -1,198 +1,189 @@
use alloy::{primitives::Address, providers::ProviderBuilder};
use bus::Bus;
use std::str::FromStr;
use url::Url;
use alloy::providers::ProviderBuilder;
use clap::Parser;
use openmls::framing::MlsMessageIn;
use std::{error::Error, fs::File, io::Read};
use tls_codec::Deserialize;
use tokio::sync::mpsc;
use tokio_util::sync::CancellationToken;
use openmls::framing::{MlsMessageIn, MlsMessageInBody};
use de_mls::{get_contract_address, user::User};
use sc_key_store::sc_ks::*;
use de_mls::{
cli::*,
user::{User, UserError},
};
#[tokio::main]
async fn main() {
let storage_address = Address::from_str("0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512").unwrap();
let (alice_address, alice_wallet) = alice_addr_test();
let alice_provider = ProviderBuilder::new()
async fn main() -> Result<(), Box<dyn Error>> {
let token = CancellationToken::new();
let (cli_tx, mut cli_gr_rx) = mpsc::channel::<Commands>(100);
let args = Args::parse();
let (user_address, wallet, storage_address) = get_user_data(&args)?;
let client_provider = ProviderBuilder::new()
.with_recommended_fillers()
.wallet(alice_wallet)
.on_http(Url::from_str("http://localhost:8545").unwrap());
.wallet(wallet)
.on_http(args.storage_url);
let (bob_address, bob_wallet) = bob_addr_test();
let bob_provider = ProviderBuilder::new()
.with_recommended_fillers()
.wallet(bob_wallet)
.on_http(Url::from_str("http://localhost:8545").unwrap());
// This channel for message before adding to group.
// Message are still encrypted, but this channel not attached to any group
let mut m: Bus<MlsMessageIn> = Bus::new(10);
let mut a_r = m.add_rx();
let mut b_r = m.add_rx();
//// Create user Alice
println!("Start Register Alice");
let res = User::new(
alice_address.as_slice(),
alice_provider.clone(),
//// Create user
let mut user = User::new(
user_address.as_slice(),
client_provider.clone(),
storage_address,
)
.await;
assert!(res.is_ok());
let mut a_user = res.unwrap();
let res = a_user.register().await;
assert!(res.is_ok());
println!("Register Alice successfully");
//////
.await?;
//// Create user Bob
println!("Start Register Bob");
let res = User::new(
bob_address.as_slice(),
bob_provider.clone(),
storage_address,
)
.await;
assert!(res.is_ok());
let mut b_user = res.unwrap();
let res = b_user.register().await;
assert!(res.is_ok());
println!("Register Bob successfully");
//////
let (messages_tx, messages_rx) = mpsc::channel::<Msg>(100);
messages_tx
.send(Msg::Input(Message::System(format!(
"Hello, {:}",
user_address
))))
.await?;
//// Alice create group: Alice_Group
println!("Start create group");
let group_name = String::from_str("Alice_Group").unwrap();
let res = a_user.create_group(group_name.clone()).await;
assert!(res.is_ok());
println!("Create group successfully");
//////
let messages_tx2 = messages_tx.clone();
let event_token = token.clone();
let h1 = tokio::spawn(async move { event_handler(messages_tx2, cli_tx, event_token).await });
//// Alice invite Bob
println!("Alice inviting Bob");
let welcome = a_user
.invite(b_user.user_wallet_address().as_slice(), group_name.clone())
.await;
assert!(welcome.is_ok());
// Alice should skip message with invite update because she already update her instance
// It is failed because of wrong epoch
let res = a_user.recieve_msg(group_name.clone()).await;
assert!(res.is_err());
let res_msg_tx = messages_tx.clone();
let main_token = token.clone();
let h2 = tokio::spawn(async move {
let (redis_tx, mut redis_rx) = mpsc::channel::<Vec<u8>>(100);
loop {
tokio::select! {
Some(val) = redis_rx.recv() =>{
let res = user.receive_msg(val).await;
match res {
Ok(msg) => {
match msg {
Some(m) => res_msg_tx.send(Msg::Input(Message::Incoming(m.group, m.author, m.message))).await?,
None => continue
}
},
Err(err) => {
res_msg_tx
.send(Msg::Input(Message::Error(err.to_string())))
.await?;
},
};
}
Some(command) = cli_gr_rx.recv() => {
// res_msg_tx.send(Msg::Input(Message::System(format!("Get command: {:?}", command)))).await?;
match command {
Commands::CreateGroup { group_name } => {
let res = user.create_group(group_name.clone()).await;
match res {
Ok(mut br) => {
let msg = format!("Successfully create group: {:?}", group_name.clone());
res_msg_tx.send(Msg::Input(Message::System(msg))).await?;
//// Send welcome message to system broadcast. Only Bob can use it
m.broadcast(welcome.unwrap());
let _ = a_r.recv();
let welc = b_r.recv();
assert!(welc.is_ok());
let _ = match welc.unwrap().extract() {
MlsMessageInBody::Welcome(welcome) => {
let res = b_user.join_group(welcome).await;
assert!(res.is_ok());
Ok(())
let redis_tx = redis_tx.clone();
tokio::spawn(async move {
while let Ok(msg) = br.recv().await {
let bytes: Vec<u8> = msg.value.convert()?;
redis_tx.send(bytes).await?;
}
Ok::<_, CliError>(())
});
},
Err(err) => {
res_msg_tx
.send(Msg::Input(Message::Error(err.to_string())))
.await?;
},
};
},
Commands::Invite { group_name, user_wallet } => {
let res = user.invite(user_wallet, group_name.clone()).await;
match res {
Ok(_) => {
let msg = format!("Invite {:} to the group {:}\n",
user_address, group_name
);
res_msg_tx.send(Msg::Input(Message::System(msg))).await?;
},
Err(err) => {
res_msg_tx
.send(Msg::Input(Message::Error(err.to_string())))
.await?;
},
};
},
Commands::JoinGroup { group_name } => {
let mut file = File::open(format!("invite_{group_name}.txt"))?;
let mut welcome = String::new();
file.read_to_string(&mut welcome).unwrap();
let wbytes = hex::decode(welcome).unwrap();
let welc = MlsMessageIn::tls_deserialize_bytes(wbytes).unwrap();
let welcome = welc.into_welcome();
if welcome.is_some() {
let res = user.join_group(welcome.unwrap()).await;
match res {
Ok(mut buf) => {
let msg = format!("Succesfully join to the group: {:#?}", buf.1);
res_msg_tx.send(Msg::Input(Message::System(msg))).await?;
let redis_tx = redis_tx.clone();
tokio::spawn(async move {
while let Ok(msg) = buf.0.recv().await {
let bytes: Vec<u8> = msg.value.convert()?;
redis_tx.send(bytes).await?;
}
Ok::<_, CliError>(())
});
},
Err(err) => {
res_msg_tx
.send(Msg::Input(Message::Error(err.to_string())))
.await?;
},
};
} else {
res_msg_tx
.send(Msg::Input(Message::Error(UserError::EmptyWelcomeMessageError.to_string())))
.await?;
}
},
Commands::SendMessage { group_name, msg } => {
let message = msg.join(" ");
let res = user.send_msg(&message, group_name.clone(), user.identity.to_string()).await;
match res {
Ok(_) => {
res_msg_tx.send(Msg::Input(Message::Mine(group_name, user.identity.to_string(), message ))).await?;
},
Err(err) => {
res_msg_tx
.send(Msg::Input(Message::Error(err.to_string())))
.await?;
},
};
},
Commands::Exit => {
res_msg_tx.send(Msg::Input(Message::System("Bye!".to_string()))).await?;
break
},
}
}
_ = main_token.cancelled() => {
break;
}
else => {
res_msg_tx.send(Msg::Input(Message::System("Something went wrong".to_string()))).await?;
break
}
};
}
_ => Err("do nothing".to_string()),
};
println!("Bob successfully join to the group");
/////
Ok::<_, CliError>(())
});
//// Bob send message and Alice recieve it
let res = b_user.send_msg("Hi!", group_name.clone()).await;
assert!(res.is_ok());
let h3 = tokio::spawn(async move { terminal_handler(messages_rx, token).await });
// Bob also get the message but he cant decrypt it (regarding the mls rfc)
let res = b_user.recieve_msg(group_name.clone()).await;
// Expected error with invalid decryption
assert!(res.is_err());
let handler_res = tokio::join!(h1, h2, h3);
handler_res.0??;
handler_res.1??;
handler_res.2??;
let res = a_user.recieve_msg(group_name.clone()).await;
assert!(res.is_ok());
/////
//// Alice send message and Bob recieve it
let res = a_user.send_msg("Hi Bob!", group_name.clone()).await;
assert!(res.is_ok());
let res = a_user.recieve_msg(group_name.clone()).await;
assert!(res.is_err());
let res = b_user.recieve_msg(group_name.clone()).await;
assert!(res.is_ok());
/////
let msg = a_user.read_msgs(group_name.clone());
println!("Alice recieve_msgs: {:#?}", msg);
let msg = b_user.read_msgs(group_name.clone());
println!("Bob recieve_msgs: {:#?}", msg);
let (carla_address, carla_wallet) = carla_addr_test();
let carla_provider = ProviderBuilder::new()
.with_recommended_fillers()
.wallet(carla_wallet)
.on_http(Url::from_str("http://localhost:8545").unwrap());
let mut c_r = m.add_rx();
//// Create user Alice
println!("Start Register Carla");
let res = User::new(
carla_address.as_slice(),
carla_provider.clone(),
storage_address,
)
.await;
assert!(res.is_ok());
let mut c_user = res.unwrap();
let res = c_user.register().await;
assert!(res.is_ok());
println!("Register Carla successfully");
//////
//// Alice invite Carla
println!("Alice inviting Carla");
let welcome = a_user
.invite(c_user.user_wallet_address().as_slice(), group_name.clone())
.await;
assert!(welcome.is_ok());
// Alice should skip message with invite update because she already update her instance
// It is failed because of wrong epoch
let res = a_user.recieve_msg(group_name.clone()).await;
assert!(res.is_err());
let res = b_user.recieve_msg(group_name.clone()).await;
assert!(res.is_ok());
//// Send welcome message to system broadcast. Only Bob can use it
m.broadcast(welcome.unwrap());
let _ = a_r.recv();
let _ = b_r.recv();
let welc = c_r.recv();
assert!(welc.is_ok());
let _ = match welc.unwrap().extract() {
MlsMessageInBody::Welcome(welcome) => {
let res = c_user.join_group(welcome).await;
assert!(res.is_ok());
Ok(())
}
_ => Err("do nothing".to_string()),
};
println!("Carla successfully join to the group");
/////
//// Carla send message and Alice and Bob recieve it
let res = c_user.send_msg("Hi all!", group_name.clone()).await;
assert!(res.is_ok());
let res = c_user.recieve_msg(group_name.clone()).await;
assert!(res.is_err());
let res = a_user.recieve_msg(group_name.clone()).await;
assert!(res.is_ok());
let res = b_user.recieve_msg(group_name.clone()).await;
assert!(res.is_ok());
////
let msg = a_user.read_msgs(group_name.clone());
println!("Alice recieve_msgs: {:#?}", msg);
let msg = b_user.read_msgs(group_name.clone());
println!("Bob recieve_msgs: {:#?}", msg);
let msg = c_user.read_msgs(group_name.clone());
println!("Carla recieve_msgs: {:#?}", msg);
Ok(())
}

View File

@@ -1,16 +1,30 @@
use std::{
borrow::BorrowMut, cell::RefCell, collections::HashMap, str, str::Utf8Error,
string::FromUtf8Error,
use alloy::{
hex::{self, FromHexError, ToHexExt},
network::Network,
primitives::Address,
providers::Provider,
transports::Transport,
};
use alloy::{network::Network, primitives::Address, providers::Provider, transports::Transport};
use fred::types::Message;
use openmls::{group::*, prelude::*};
use openmls_rust_crypto::MemoryKeyStoreError;
use std::{
borrow::BorrowMut,
cell::RefCell,
collections::HashMap,
fmt::Display,
fs::File,
io::Write,
str::{from_utf8, FromStr, Utf8Error},
string::FromUtf8Error,
};
use tokio::sync::broadcast::Receiver;
use ds::ds::*;
use mls_crypto::openmls_provider::*;
use sc_key_store::{local_ks::LocalCache, sc_ks::ScKeyStorage, *};
// use waku_bindings::*;
//
use crate::conversation::*;
use crate::identity::{Identity, IdentityError};
@@ -19,14 +33,20 @@ pub struct Group {
group_name: String,
conversation: Conversation,
mls_group: RefCell<MlsGroup>,
epoch: GroupEpoch,
rc_client: RClient,
// pubsub_topic: WakuPubSubTopic,
// content_topics: Vec<WakuContentTopic>,
}
impl Display for Group {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Group: {:#?}", self.group_name)
}
}
pub struct User<T, P, N> {
pub(crate) identity: Identity,
pub(crate) groups: HashMap<String, Group>,
pub identity: Identity,
pub groups: HashMap<String, Group>,
provider: MlsCryptoProvider,
sc_ks: ScKeyStorage<T, P, N>,
local_ks: LocalCache,
@@ -47,25 +67,26 @@ where
) -> Result<Self, UserError> {
let crypto = MlsCryptoProvider::default();
let id = Identity::new(CIPHERSUITE, &crypto, user_wallet_address)?;
Ok(User {
let mut user = User {
groups: HashMap::new(),
identity: id,
provider: crypto,
local_ks: LocalCache::empty_key_store(user_wallet_address),
sc_ks: ScKeyStorage::new(provider, sc_storage_address),
// contacts: HashMap::new(),
})
};
user.register().await?;
Ok(user)
}
pub fn user_wallet_address(&self) -> Vec<u8> {
self.identity.identity()
}
pub async fn create_group(&mut self, group_name: String) -> Result<(), UserError> {
pub async fn create_group(
&mut self,
group_name: String,
) -> Result<Receiver<Message>, UserError> {
let group_id = group_name.as_bytes();
if self.groups.contains_key(&group_name) {
return Err(UserError::UnknownGroupError(group_name));
return Err(UserError::AlreadyExistedGroupError(group_name));
}
let group_config = MlsGroupConfig::builder()
@@ -80,21 +101,23 @@ where
self.identity.credential_with_key.clone(),
)?;
let rc = RClient::new_for_group(group_name.clone()).await?;
let (rc, broadcaster) = RClient::new_for_group(group_name.clone()).await?;
let epoch = mls_group.epoch();
let group = Group {
group_name: group_name.clone(),
conversation: Conversation::default(),
mls_group: RefCell::new(mls_group),
epoch,
rc_client: rc,
// pubsub_topic: WakuPubSubTopic::new(),
// content_topics: Vec::new(),
};
self.groups.insert(group_name, group);
Ok(())
Ok(broadcaster)
}
pub async fn register(&mut self) -> Result<(), UserError> {
async fn register(&mut self) -> Result<(), UserError> {
let kp = self.key_packages();
self.sc_ks
.borrow_mut()
@@ -114,9 +137,11 @@ where
pub async fn invite(
&mut self,
user_wallet_address: &[u8],
user_wallet: String,
group_name: String,
) -> Result<MlsMessageIn, UserError> {
) -> Result<(), UserError> {
let user_address = Address::from_str(&user_wallet)?;
let user_wallet_address = user_address.as_slice();
// First we need to get the key package for {id} from the DS.
if !self
.sc_ks
@@ -146,71 +171,70 @@ where
&[joiner_key_package],
)?;
group.rc_client.msg_send(out_messages).await?;
group
.rc_client
.msg_send(out_messages, self.identity.to_string())
.await?;
// Second, process the invitation on our end.
group
.mls_group
.borrow_mut()
.merge_pending_commit(&self.provider)?;
group.epoch = group.mls_group.borrow_mut().epoch();
// Put sending welcome by p2p here
let bytes = welcome.tls_serialize_detached()?;
let string = bytes.encode_hex();
Ok(welcome.into())
let mut file = File::create(format!("invite_{group_name}.txt"))?;
file.write_all(string.as_bytes())?;
Ok(())
}
pub async fn recieve_msg(&mut self, group_name: String) -> Result<(), UserError> {
pub async fn receive_msg(
&mut self,
msg_bytes: Vec<u8>,
) -> Result<Option<ConversationMessage>, UserError> {
let buf: SenderStruct = serde_json::from_slice(&msg_bytes)?;
if buf.sender == self.identity.to_string() {
return Ok(None);
}
let res = MlsMessageIn::tls_deserialize_bytes(&buf.msg)?;
let msg = match res.extract() {
MlsMessageInBody::PrivateMessage(message) => {
self.process_protocol_msg(message.into())?
}
MlsMessageInBody::PublicMessage(message) => {
self.process_protocol_msg(message.into())?
}
_ => return Err(UserError::MessageTypeError),
};
Ok(msg)
}
pub fn process_protocol_msg(
&mut self,
message: ProtocolMessage,
) -> Result<Option<ConversationMessage>, UserError> {
let group_name = from_utf8(message.group_id().as_slice())?.to_string();
let group = match self.groups.get_mut(&group_name) {
Some(g) => g,
None => return Err(UserError::UnknownGroupError(group_name)),
};
let msg = group.rc_client.msg_recv().await?;
match msg.extract() {
MlsMessageInBody::Welcome(_welcome) => {
// Now irrelevant because message are attached to group
// self.join_group(welcome, Rc::clone(&group.ds_node))?;
}
MlsMessageInBody::PrivateMessage(message) => {
self.process_protocol_msg(message.into())?;
}
MlsMessageInBody::PublicMessage(message) => {
self.process_protocol_msg(message.into())?;
}
_ => return Err(UserError::MessageTypeError),
}
Ok(())
}
fn process_protocol_msg(&mut self, message: ProtocolMessage) -> Result<(), UserError> {
let group_name = str::from_utf8(message.group_id().as_slice())?;
let group = match self.groups.get_mut(group_name) {
Some(g) => g,
None => return Err(UserError::UnknownGroupError(group_name.to_string())),
};
let mut mls_group = group.mls_group.borrow_mut();
let processed_message = mls_group.process_message(&self.provider, message)?;
let processed_message_credential: Credential = processed_message.credential().clone();
match processed_message.into_content() {
ProcessedMessageContent::ApplicationMessage(application_message) => {
let sender_name = {
let user_id = mls_group.members().find_map(|m| {
if m.credential.identity()
== processed_message_credential.identity()
&& (self
.identity
.credential_with_key
.signature_key
.as_slice()
if m.credential.identity() == processed_message_credential.identity()
&& (self.identity.credential_with_key.signature_key.as_slice()
!= m.signature_key.as_slice())
{
println!("process ApplicationMessage: read sender name from credential identity for group {} ", group.group_name);
Some(
format!("{:?}", m.credential.identity())
)
Some(hex::encode(m.credential.identity()))
} else {
None
}
@@ -219,10 +243,12 @@ where
};
let conversation_message = ConversationMessage::new(
String::from_utf8(application_message.into_bytes())?,
group_name,
sender_name,
String::from_utf8(application_message.into_bytes())?,
);
group.conversation.add(conversation_message);
group.conversation.add(conversation_message.clone());
return Ok(Some(conversation_message));
}
ProcessedMessageContent::ProposalMessage(_proposal_ptr) => (),
ProcessedMessageContent::ExternalJoinProposalMessage(_external_proposal_ptr) => (),
@@ -232,20 +258,23 @@ where
remove_proposal = true;
}
mls_group.merge_staged_commit(&self.provider, *commit_ptr)?;
group.epoch = group.mls_group.borrow_mut().epoch();
if remove_proposal {
println!(
"update::Processing StagedCommitMessage removing {} from group {} ",
self.identity.to_string(),
group.group_name
);
return Ok(());
// here we need to remove group instance locally and
// also remove correspond key package from local storage ans sc storage
return Ok(None);
}
}
};
Ok(())
Ok(None)
}
pub async fn send_msg(&mut self, msg: &str, group_name: String) -> Result<(), UserError> {
pub async fn send_msg(
&mut self,
msg: &str,
group_name: String,
sender: String,
) -> Result<(), UserError> {
let group = match self.groups.get_mut(&group_name) {
Some(g) => g,
None => return Err(UserError::UnknownGroupError(group_name)),
@@ -257,11 +286,14 @@ where
msg.as_bytes(),
)?;
group.rc_client.msg_send(message_out).await?;
group.rc_client.msg_send(message_out, sender).await?;
Ok(())
}
pub async fn join_group(&mut self, welcome: Welcome) -> Result<(), UserError> {
pub async fn join_group(
&mut self,
welcome: Welcome,
) -> Result<(Receiver<Message>, String), UserError> {
let group_config = MlsGroupConfig::builder()
.use_ratchet_tree_extension(true)
.build();
@@ -271,47 +303,49 @@ where
let group_id = mls_group.group_id().to_vec();
let group_name = String::from_utf8(group_id)?;
let rc = RClient::new_for_group(group_name.clone()).await?;
let (rc, br) = RClient::new_for_group(group_name.clone()).await?;
let epoch = mls_group.epoch();
let group = Group {
group_name: group_name.clone(),
conversation: Conversation::default(),
mls_group: RefCell::new(mls_group),
epoch,
rc_client: rc,
};
match self.groups.insert(group_name, group) {
match self.groups.insert(group_name.clone(), group) {
Some(old) => Err(UserError::AlreadyExistedGroupError(old.group_name)),
None => Ok(()),
None => Ok((br, group_name)),
}
}
pub async fn remove(&mut self, name: String, group_name: String) -> Result<(), UserError> {
// Get the group ID
let group = match self.groups.get_mut(&group_name) {
Some(g) => g,
None => return Err(UserError::UnknownGroupError(group_name)),
};
// pub async fn remove(&mut self, name: String, group_name: String) -> Result<(), UserError> {
// // Get the group ID
// let group = match self.groups.get_mut(&group_name) {
// Some(g) => g,
// None => return Err(UserError::UnknownGroupError(group_name)),
// };
// Get the user leaf index
let leaf_index = group.find_member_index(name)?;
// // Get the user leaf index
// let leaf_index = group.find_member_index(name)?;
// Remove operation on the mls group
let (remove_message, _welcome, _group_info) = group.mls_group.borrow_mut().remove_members(
&self.provider,
&self.identity.signer,
&[leaf_index],
)?;
// // Remove operation on the mls group
// let (remove_message, _welcome, _group_info) = group.mls_group.borrow_mut().remove_members(
// &self.provider,
// &self.identity.signer,
// &[leaf_index],
// )?;
group.rc_client.msg_send(remove_message).await?;
// group.rc_client.msg_send(remove_message).await?;
// Second, process the removal on our end.
group
.mls_group
.borrow_mut()
.merge_pending_commit(&self.provider)?;
// // Second, process the removal on our end.
// group
// .mls_group
// .borrow_mut()
// .merge_pending_commit(&self.provider)?;
Ok(())
}
// Ok(())
// }
/// Return the last 100 messages sent to the group.
pub fn read_msgs(
@@ -327,30 +361,45 @@ where
},
)
}
pub fn group_members(&self, group_name: String) -> Result<Vec<String>, UserError> {
let group = match self.groups.get(&group_name) {
Some(g) => g,
None => return Err(UserError::UnknownGroupError(group_name)),
};
Ok(group.group_members(self.identity.signature_pub_key().as_slice()))
}
pub fn user_groups(&self) -> Result<Vec<String>, UserError> {
if self.groups.is_empty() {
return Ok(Vec::default());
}
Ok(self.groups.keys().map(|k| k.to_owned()).collect())
}
}
impl Group {
/// Get a member
fn find_member_index(&self, name: String) -> Result<LeafNodeIndex, GroupError> {
fn find_member_index(&self, user_id: String) -> Result<LeafNodeIndex, GroupError> {
let member = self
.mls_group
.borrow()
.members()
.find(|m| m.credential.identity().eq(name.as_bytes()));
.find(|m| m.credential.identity().eq(user_id.as_bytes()));
match member {
Some(m) => Ok(m.index),
None => Err(GroupError::UnknownGroupMemberError(name)),
None => Err(GroupError::UnknownGroupMemberError(user_id)),
}
}
fn group_members(&self, user_signature: &[u8]) -> Vec<Vec<u8>> {
pub fn group_members(&self, user_signature: &[u8]) -> Vec<String> {
self.mls_group
.borrow()
.members()
.filter(|m| m.signature_key == user_signature)
.map(|m| m.credential.identity().to_vec())
.collect::<Vec<Vec<u8>>>()
.map(|m| hex::encode(m.credential.identity()))
.collect::<Vec<String>>()
}
}
@@ -370,6 +419,9 @@ pub enum UserError {
MessageTypeError,
#[error("Unknown user")]
UnknownUserError,
#[error("Empty welcome message")]
EmptyWelcomeMessageError,
#[error("Delivery Service error: {0}")]
DeliveryServiceError(#[from] DeliveryServiceError),
#[error(transparent)]
@@ -378,6 +430,7 @@ pub enum UserError {
KeyStoreError(#[from] KeyStoreError),
#[error("Identity error: {0}")]
IdentityError(#[from] IdentityError),
#[error("Something wrong while creating Mls group: {0}")]
MlsGroupCreationError(#[from] NewGroupError<MemoryKeyStoreError>),
#[error("Something wrong while adding member to Mls group: {0}")]
@@ -394,10 +447,20 @@ pub enum UserError {
MlsWelcomeError(#[from] WelcomeError<MemoryKeyStoreError>),
#[error("Failed to remove member from group: {0}")]
MlsRemoveMembersError(#[from] RemoveMembersError<MemoryKeyStoreError>),
#[error("Parse String UTF8 error: {0}")]
ParseUTF8Error(#[from] FromUtf8Error),
#[error("Parse str UTF8 error: {0}")]
ParseStrUTF8Error(#[from] Utf8Error),
#[error("Json error: {0}")]
JsonError(#[from] serde_json::Error),
#[error("Serialization problem: {0}")]
TlsError(#[from] tls_codec::Error),
#[error("Unable to parce the address: {0}")]
AlloyFromHexError(#[from] FromHexError),
#[error("Write to stdout error")]
IoError(#[from] std::io::Error),
#[error("Unknown error: {0}")]
Other(anyhow::Error),
}