Skip to content

Commit

Permalink
Rust implementation for some Cairo types
Browse files Browse the repository at this point in the history
  • Loading branch information
ctoyan committed Apr 24, 2024
1 parent 07bc88f commit 26d6011
Show file tree
Hide file tree
Showing 6 changed files with 835 additions and 13 deletions.
2 changes: 2 additions & 0 deletions ampd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ serde_json = { workspace = true }
serde_with = "3.2.0"
service-registry = { workspace = true }
sha3 = { workspace = true }
starknet-core = "0.10.0"
starknet-providers = "0.10.0"
sui-json-rpc-types = { git = "https://github.com/mystenlabs/sui", tag = "mainnet-v1.14.2" }
sui-types = { git = "https://github.com/mystenlabs/sui", features = ["test-utils"], tag = "mainnet-v1.14.2" }
# Need to switch to our own fork of tendermint and tendermint-rpc due to event attribute value being nullable.
Expand Down
27 changes: 14 additions & 13 deletions ampd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,28 @@ use std::pin::Pin;
use std::time::Duration;

use block_height_monitor::BlockHeightMonitor;
use broadcaster::accounts::account;
use broadcaster::Broadcaster;
use connection_router_api::ChainName;
use cosmos_sdk_proto::cosmos::{
auth::v1beta1::query_client::QueryClient, tx::v1beta1::service_client::ServiceClient,
};
use cosmos_sdk_proto::cosmos::auth::v1beta1::query_client::QueryClient;
use cosmos_sdk_proto::cosmos::tx::v1beta1::service_client::ServiceClient;
use error_stack::{report, FutureExt, Result, ResultExt};
use event_processor::EventHandler;
use events::Event;
use evm::finalizer::{pick, Finalization};
use evm::json_rpc::EthereumClient;
use queue::queued_broadcaster::{QueuedBroadcaster, QueuedBroadcasterDriver};
use state::StateUpdater;
use thiserror::Error;
use tofnd::grpc::{MultisigClient, SharableEcdsaClient};
use tokio::signal::unix::{signal, SignalKind};
use tokio::sync::oneshot;
use tokio_stream::Stream;
use tokio_util::sync::CancellationToken;
use tracing::info;

use crate::asyncutil::task::{CancellableTask, TaskError, TaskGroup};
use broadcaster::{accounts::account, Broadcaster};
use event_processor::EventHandler;
use events::Event;
use queue::queued_broadcaster::{QueuedBroadcaster, QueuedBroadcasterDriver};
use state::StateUpdater;
use tofnd::grpc::{MultisigClient, SharableEcdsaClient};
use tracing::{info, warn};
use types::TMAddress;

use crate::asyncutil::task::{CancellableTask, TaskError, TaskGroup};
use crate::config::Config;
use crate::state::State;

Expand All @@ -41,6 +40,7 @@ mod handlers;
mod health_check;
mod json_rpc;
mod queue;
mod starknet;
pub mod state;
mod sui;
mod tm_client;
Expand Down Expand Up @@ -431,7 +431,8 @@ where
broadcaster.run().change_context(Error::Broadcaster)
}))
.add_task(CancellableTask::create(|_| async move {
// assert: the state updater only stops when all handlers that are updating their states have stopped
// assert: the state updater only stops when all handlers that are updating
// their states have stopped
state_tx
.send(state_updater.run().await)
.map_err(|_| report!(Error::ReturnState))
Expand Down
1 change: 1 addition & 0 deletions ampd/src/starknet/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod types;
250 changes: 250 additions & 0 deletions ampd/src/starknet/types/array_span.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
use starknet_core::types::{FieldElement, ValueOutOfRangeError};
use thiserror::Error;

/// Applies for bot a cairo Array and a Span
#[derive(Debug)]
pub struct ArraySpan {
pub bytes: Vec<u8>,
}

#[derive(Error, Debug)]
pub enum ArraySpanError {
#[error("Invalid array/span length")]
InvalidLength,
#[error("Failed to parse felt - {0}")]
ParsingFelt(#[from] ValueOutOfRangeError),
}

impl TryFrom<Vec<FieldElement>> for ArraySpan {
type Error = ArraySpanError;

fn try_from(data: Vec<FieldElement>) -> Result<Self, Self::Error> {
// First element is always the array length.
// We also have to go from `u32` to usize, because
// there's no direct `usize` From impl.
let arr_length: u32 = match data[0].try_into() {
Ok(al) => al,
Err(err) => return Err(ArraySpanError::ParsingFelt(err)),
};

// -1 because we have to offset the first element (the length itself)
let is_arr_el_count_valid = usize::try_from(arr_length)
.map(|count| count == data.len() - 1)
.unwrap_or(false);

if !is_arr_el_count_valid {
return Err(ArraySpanError::InvalidLength);
}

let bytes_parse: Result<Vec<u8>, ArraySpanError> = match data.get(1..) {
Some(b) => b,
None => return Err(ArraySpanError::InvalidLength),
}
.to_vec()
.into_iter()
.map(|e| {
let word_count: u8 = match e.try_into() {
Ok(wc) => wc,
Err(err) => return Err(ArraySpanError::ParsingFelt(err)),
};

Ok(word_count)
})
.collect();

let bytes = match bytes_parse {
Ok(b) => b,
Err(e) => return Err(e),
};

Ok(ArraySpan { bytes })
}
}

#[cfg(test)]
mod array_span_tests {
use std::str::FromStr;

use starknet_core::types::FieldElement;

use crate::starknet::types::array_span::ArraySpan;

#[test]
fn try_from_valid_zeros() {
// the string "hello", but FieldElement is bigger than u8::max
let data = vec![FieldElement::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000000",
)
.unwrap()];

let array_span = ArraySpan::try_from(data).unwrap();
assert_eq!(array_span.bytes, Vec::<u8>::new());
}

#[test]
fn try_from_failed_to_parse_element_to_u8() {
// the string "hello", but FieldElement is bigger than u8::max
let data = vec![
FieldElement::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000005",
)
.unwrap(),
FieldElement::from_str(
"0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
)
.unwrap(),
FieldElement::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000065",
)
.unwrap(),
FieldElement::from_str(
"0x000000000000000000000000000000000000000000000000000000000000006c",
)
.unwrap(),
FieldElement::from_str(
"0x000000000000000000000000000000000000000000000000000000000000006c",
)
.unwrap(),
FieldElement::from_str(
"0x000000000000000000000000000000000000000000000000000000000000006f",
)
.unwrap(),
];

let array_span = ArraySpan::try_from(data);
assert!(array_span.is_err());
}

#[test]
fn try_from_failed_to_parse_elements_length_to_u32() {
// the string "hello", but element counte bigger than u32::max
let data = vec![
FieldElement::from_str(
"0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
)
.unwrap(),
FieldElement::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000068",
)
.unwrap(),
FieldElement::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000065",
)
.unwrap(),
FieldElement::from_str(
"0x000000000000000000000000000000000000000000000000000000000000006c",
)
.unwrap(),
FieldElement::from_str(
"0x000000000000000000000000000000000000000000000000000000000000006c",
)
.unwrap(),
FieldElement::from_str(
"0x000000000000000000000000000000000000000000000000000000000000006f",
)
.unwrap(),
];

let array_span = ArraySpan::try_from(data);
assert!(array_span.is_err());
}

#[test]
fn try_from_invalid_number_of_elements() {
// the string "hello", but with only 4 bytes
let data = vec![
FieldElement::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000005",
)
.unwrap(),
FieldElement::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000068",
)
.unwrap(),
FieldElement::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000065",
)
.unwrap(),
FieldElement::from_str(
"0x000000000000000000000000000000000000000000000000000000000000006c",
)
.unwrap(),
FieldElement::from_str(
"0x000000000000000000000000000000000000000000000000000000000000006c",
)
.unwrap(),
];

let array_span = ArraySpan::try_from(data);
assert!(array_span.is_err());
}

#[test]
fn try_from_invalid_declared_length() {
// the string "hello", with correct number of bytes, but only 4 declared,
// instead of 5
let data = vec![
FieldElement::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000004",
)
.unwrap(),
FieldElement::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000068",
)
.unwrap(),
FieldElement::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000065",
)
.unwrap(),
FieldElement::from_str(
"0x000000000000000000000000000000000000000000000000000000000000006c",
)
.unwrap(),
FieldElement::from_str(
"0x000000000000000000000000000000000000000000000000000000000000006c",
)
.unwrap(),
FieldElement::from_str(
"0x000000000000000000000000000000000000000000000000000000000000006f",
)
.unwrap(),
];

let array_span = ArraySpan::try_from(data);
assert!(array_span.is_err());
}

#[test]
fn try_from_valid() {
// the string "hello"
let data = vec![
FieldElement::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000005",
)
.unwrap(),
FieldElement::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000068",
)
.unwrap(),
FieldElement::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000065",
)
.unwrap(),
FieldElement::from_str(
"0x000000000000000000000000000000000000000000000000000000000000006c",
)
.unwrap(),
FieldElement::from_str(
"0x000000000000000000000000000000000000000000000000000000000000006c",
)
.unwrap(),
FieldElement::from_str(
"0x000000000000000000000000000000000000000000000000000000000000006f",
)
.unwrap(),
];

let array_span = ArraySpan::try_from(data).unwrap();
assert_eq!(array_span.bytes, vec![104, 101, 108, 108, 111]);
}
}
Loading

0 comments on commit 26d6011

Please sign in to comment.