Skip to content

Commit

Permalink
validate ens names upon message submit
Browse files Browse the repository at this point in the history
  • Loading branch information
aditiharini committed Dec 18, 2024
1 parent 35d5878 commit b54d9f0
Show file tree
Hide file tree
Showing 10 changed files with 1,579 additions and 193 deletions.
1,571 changes: 1,415 additions & 156 deletions Cargo.lock

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,15 @@ tracing = "0.1.40"
thiserror = "1.0.66"
reqwest = { version = "0.12.9", features = ["json"] }
figment = { version = "0.10.19", features = ["env", "toml"] }
alloy = { version = "0.5.4", features = ["full"] }
futures-util = "0.3.31"
url = "2.5.3"
alloy-transport = "0.5.4"
alloy-sol-types = "0.8.11"
alloy-transport = "0.8.0"
alloy-transport-http = "0.8.0"
alloy-sol-types = { version = "0.8.15", features = ["json"] }
alloy-provider = "0.8.0"
alloy-rpc-types = "0.8.0"
alloy-primitives = "0.8.14"
alloy-contract = "0.8.0"
ed25519-dalek = "2.1.1"
pre-commit = "0.5.2"
rocksdb = {git = "https://github.com/rust-rocksdb/rust-rocksdb.git", rev="1cf906dc4087f06631820f13855e6b27bd21b972", features=["multi-threaded-cf"]}
Expand All @@ -52,6 +56,7 @@ humantime = "2.1.0"
itertools = "0.13.0"
cadence = "1.5.0"
tempfile = "3.13.0"
foundry-common = { git = "https://github.com/foundry-rs/foundry", version = "0.2.0" }

[build-dependencies]
tonic-build = "0.9.2"
Expand Down
5 changes: 5 additions & 0 deletions src/bin/setup_local_testnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ struct Args {
#[arg(long, value_parser = parse_duration, default_value = "250ms")]
propose_value_delay: Duration,

#[arg(long, default_value = "")]
l1_rpc_url: String,

/// Statsd prefix. note: node ID will be appended before config file written
#[arg(long, default_value = "snapchain")]
statsd_prefix: String,
Expand Down Expand Up @@ -66,11 +69,13 @@ async fn main() {
let statsd_prefix = format!("{}{}", args.statsd_prefix, id);
let statsd_addr = args.statsd_addr.clone();
let statsd_use_tags = args.statsd_use_tags;
let l1_rpc_url = args.l1_rpc_url.clone();

let config_file_content = format!(
r#"
rpc_address="{rpc_address}"
rocksdb_dir="{db_dir}"
l1_rpc_url="{l1_rpc_url}"
[statsd]
prefix="{statsd_prefix}"
Expand Down
2 changes: 2 additions & 0 deletions src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub struct Config {
pub clear_db: bool,
pub statsd: StatsdConfig,
pub trie_branching_factor: u32,
pub l1_rpc_url: String,
}

impl Default for Config {
Expand All @@ -54,6 +55,7 @@ impl Default for Config {
clear_db: false,
statsd: StatsdConfig::default(),
trie_branching_factor: 16,
l1_rpc_url: "".to_string(),
}
}
}
Expand Down
50 changes: 35 additions & 15 deletions src/connectors/onchain_events/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use std::collections::HashMap;

use alloy::{
primitives::{address, Address, Bytes, FixedBytes, Uint},
providers::{Provider, ProviderBuilder, RootProvider},
rpc::types::{Filter, Log},
sol,
sol_types::SolEvent,
transports::http::{Client, Http},
};
use alloy_primitives::{address, Address, Bytes, FixedBytes, Uint};
use alloy_provider::{Provider, ProviderBuilder, RootProvider};
use alloy_rpc_types::{Filter, Log};
use alloy_sol_types::{sol, SolEvent};
use alloy_transport_http::{Client, Http};
use foundry_common::ens::EnsError;
use futures_util::stream::StreamExt;
use serde::{Deserialize, Serialize};
use thiserror::Error;
Expand Down Expand Up @@ -86,7 +84,7 @@ pub enum SubscribeError {
UnableToFindBlockByHash,
}

#[derive(Debug)]
#[derive(Clone, Debug)]
pub enum SignerEvent {
Add {
key: Bytes,
Expand All @@ -102,13 +100,13 @@ pub enum SignerEvent {
},
}

#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct SignerMigratedEvent {
#[allow(dead_code)] // TODO
migrated_at: u64,
}

#[derive(Debug)]
#[derive(Clone, Debug)]
pub enum IdRegisterEvent {
Register {
to: Address,
Expand All @@ -123,7 +121,7 @@ pub enum IdRegisterEvent {
},
}

#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct StorageRentEvent {
#[allow(dead_code)] // TODO
payer: Address,
Expand All @@ -135,15 +133,15 @@ pub struct StorageRentEvent {
expiry: u64,
}

#[derive(Debug)]
#[derive(Clone, Debug)]
pub enum EventType {
Signer(SignerEvent),
SignerMigrated { migrated_at: u64 },
IdRegister(IdRegisterEvent),
StorageRent(StorageRentEvent),
}

#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct Event {
#[allow(dead_code)] // TODO
chain_id: u64,
Expand Down Expand Up @@ -173,6 +171,28 @@ pub struct Event {
event_type: EventType,
}

pub struct L1Client {
provider: RootProvider<Http<Client>>,
}

impl L1Client {
pub fn new(rpc_url: String) -> Result<L1Client, SubscribeError> {
if rpc_url.is_empty() {
return Err(SubscribeError::EmptyRpcUrl);
}
let url = rpc_url.parse()?;
let provider = ProviderBuilder::new().on_http(url);
Ok(L1Client { provider })
}

pub async fn resolve_ens_name(&self, name: String) -> Result<Address, EnsError> {
foundry_common::ens::NameOrAddress::Name(name)
.resolve(&self.provider)
.await
}
}

#[derive(Clone)]
pub struct Subscriber {
provider: RootProvider<Http<Client>>,
onchain_events_by_block: HashMap<u64, Vec<Event>>,
Expand Down Expand Up @@ -227,7 +247,7 @@ impl Subscriber {
async fn get_block_timestamp(&self, block_hash: FixedBytes<32>) -> Result<u64, SubscribeError> {
let block = self
.provider
.get_block_by_hash(block_hash, alloy::rpc::types::BlockTransactionsKind::Hashes)
.get_block_by_hash(block_hash, alloy_rpc_types::BlockTransactionsKind::Hashes)
.await?
.ok_or(SubscribeError::UnableToFindBlockByHash)?;
Ok(block.header.timestamp)
Expand Down
3 changes: 3 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use malachite_metrics::{Metrics, SharedRegistry};
use snapchain::connectors::onchain_events::L1Client;
use snapchain::consensus::consensus::SystemMessage;
use snapchain::core::types::proto;
use snapchain::mempool::routing;
Expand Down Expand Up @@ -167,13 +168,15 @@ async fn main() -> Result<(), Box<dyn Error>> {

let rpc_block_store = block_store.clone();
tokio::spawn(async move {
let l1_client = L1Client::new(app_config.l1_rpc_url).ok();
let service = MyHubService::new(
rpc_block_store,
rpc_shard_stores,
rpc_shard_senders,
statsd_client.clone(),
app_config.consensus.num_shards,
Box::new(routing::ShardRouter {}),
l1_client,
);

let resp = Server::builder()
Expand Down
92 changes: 92 additions & 0 deletions src/network/server.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use super::rpc_extensions::{AsMessagesResponse, AsSingleMessageResponse};
use crate::connectors::onchain_events::L1Client;
use crate::core::error::HubError;
use crate::mempool::routing;
use crate::proto;
use crate::proto::hub_service_server::HubService;
use crate::proto::GetInfoResponse;
use crate::proto::HubEvent;
use crate::proto::UserNameProof;
use crate::proto::UserNameType;
use crate::proto::{Block, CastId, DbStats};
use crate::proto::{BlocksRequest, ShardChunksRequest, ShardChunksResponse, SubscribeRequest};
use crate::proto::{FidRequest, FidTimestampRequest};
Expand Down Expand Up @@ -39,6 +42,7 @@ pub struct MyHubService {
num_shards: u32,
message_router: Box<dyn routing::MessageRouter>,
statsd_client: StatsdClientWrapper,
l1_client: Option<L1Client>,
}

impl MyHubService {
Expand All @@ -49,6 +53,7 @@ impl MyHubService {
statsd_client: StatsdClientWrapper,
num_shards: u32,
message_router: Box<dyn routing::MessageRouter>,
l1_client: Option<L1Client>,
) -> Self {
Self {
block_store,
Expand All @@ -57,6 +62,7 @@ impl MyHubService {
statsd_client,
message_router,
num_shards,
l1_client,
}
}

Expand Down Expand Up @@ -111,6 +117,25 @@ impl MyHubService {
err.to_string()
)));
}

if let Some(message_data) = &message.data {
match &message_data.body {
Some(proto::message_data::Body::UserDataBody(user_data)) => {
if user_data.r#type() == proto::UserDataType::Username {
if user_data.value.ends_with(".eth") {
self.validate_ens_username(fid, user_data.value.to_string())
.await?;
}
};
}
Some(proto::message_data::Body::UsernameProofBody(proof)) => {
if proof.r#type() == UserNameType::UsernameTypeEnsL1 {
self.validate_ens_username_proof(fid, &proof).await?;
}
}
_ => {}
}
}
}

match sender
Expand Down Expand Up @@ -145,6 +170,73 @@ impl MyHubService {
)),
}
}

async fn validate_ens_username_proof(
&self,
fid: u64,
proof: &UserNameProof,
) -> Result<(), Status> {
match &self.l1_client {
None => {
// Skip validation
Err(Status::invalid_argument("unable to validate ens name"))
}
Some(l1_client) => {
let name = std::str::from_utf8(&proof.name)
.map_err(|err| Status::from_error(Box::new(err)))?;

if !name.ends_with(".eth") {
return Err(Status::invalid_argument(
"invalid ens name, doesn't end with .eth",
));
}

let resolved_ens_address = l1_client
.resolve_ens_name(name.to_string())
.await
.map_err(|err| Status::from_error(Box::new(err)))?
.to_vec();

if resolved_ens_address != proof.owner {
return Err(Status::invalid_argument("invalid ens name"));
}

let stores = self
.get_stores_for(fid)
.map_err(|err| Status::from_error(Box::new(err)))?;

let verification = VerificationStore::get_verification_add(
&stores.verification_store,
fid,
&resolved_ens_address,
)
.map_err(|err| Status::from_error(Box::new(err)))?;

match verification {
None => Err(Status::invalid_argument("invalid ens name")),
Some(_) => Ok(()),
}
}
}
}

async fn validate_ens_username(&self, fid: u64, fname: String) -> Result<(), Status> {
let stores = self
.get_stores_for(fid)
.map_err(|err| Status::from_error(Box::new(err)))?;
let proof = UserDataStore::get_username_proof(
&stores.user_data_store,
&mut RocksDbTransactionBatch::new(),
fname.as_bytes(),
)
.map_err(|err| Status::from_error(Box::new(err)))?;
match proof {
Some(proof) => self.validate_ens_username_proof(fid, &proof).await,
None => Err(Status::invalid_argument(
"missing username proof for username",
)),
}
}
}

#[tonic::async_trait]
Expand Down
1 change: 1 addition & 0 deletions src/network/server_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ mod tests {
statsd_client,
num_shards,
message_router,
None,
),
)
}
Expand Down
36 changes: 17 additions & 19 deletions src/storage/store/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -869,27 +869,25 @@ impl ShardEngine {
let fname = fname.to_string();
// TODO: validate fname string

if fname.ends_with(".eth") {
// TODO: Validate ens names
} else {
let proof = UserDataStore::get_username_proof(
&self.stores.user_data_store,
txn,
fname.as_bytes(),
)
.map_err(|e| MessageValidationError::StoreError {
inner: e,
hash: vec![],
})?;
match proof {
Some(proof) => {
if proof.fid != fid {
return Err(MessageValidationError::MissingFname);
}
}
None => {
let proof =
UserDataStore::get_username_proof(&self.stores.user_data_store, txn, fname.as_bytes())
.map_err(|e| MessageValidationError::StoreError {
inner: e,
hash: vec![],
})?;
match proof {
Some(proof) => {
if proof.fid != fid {
return Err(MessageValidationError::MissingFname);
}

if fname.ends_with(".eth") {
// TODO: Validate ens names
} else {
}
}
None => {
return Err(MessageValidationError::MissingFname);
}
}
Ok(())
Expand Down
1 change: 1 addition & 0 deletions tests/consensus_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ impl NodeForTest {
statsd_client.clone(),
num_shards,
Box::new(routing::EvenOddRouterForTest {}),
None,
);

let grpc_socket_addr: SocketAddr = addr.parse().unwrap();
Expand Down

0 comments on commit b54d9f0

Please sign in to comment.