Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Cargo.lock

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

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ sigma-stream = { git = "ssh://git@github.com/essential-contributions/sigma-tools
tempfile = "3.20.0"
tokio = { version = "1.45.1", features = ["full"] }
tower-http = { version = "0.6.6", features = ["cors"] }
tokio-util = "0.7.15"
tokio-util = "0.7.15"

kit = { path = "crates/kit", version = "0.1.0" }
1 change: 1 addition & 0 deletions crates/increment/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ axum.workspace = true
clap.workspace = true
futures.workspace = true
http.workspace = true
kit.workspace = true
reqwest.workspace = true
serde.workspace = true
serde_json.workspace = true
Expand Down
40 changes: 14 additions & 26 deletions crates/increment/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use std::collections::HashMap;

use alloy::{primitives::Address, signers::local::PrivateKeySigner, sol};
use futures::StreamExt;
use sigma_stream::stream_events;
use sigma_types::{Event, Height, Signed};
use sigma_types::{Height, Signed};
use tokio::sync::mpsc;

use crate::{
Increment::Incremented,
server::{GetCounts, GetProof, Req},
signing::sign,
};
Expand All @@ -21,7 +21,6 @@ sol!(
);

pub mod proof;
pub mod sequence;
pub mod server;
pub mod signing;
pub mod state;
Expand All @@ -41,7 +40,7 @@ pub struct Channels {

enum Message {
Req(Req),
Event(Result<Signed<Event>, std::io::Error>),
Event(Result<Incremented, anyhow::Error>),
}

impl App {
Expand Down Expand Up @@ -113,11 +112,11 @@ pub async fn run(
) -> anyhow::Result<()> {
let mut app = App::new(signer)?;

let event_stream = stream_events(&sigma_url, 0)
.await
.unwrap()
.map(Message::Event)
.boxed();
let event_stream =
kit::verify::stream_events(&sigma_url, 0, Height::MAX, sigma_poa, state::map_input)
.await?
.map(Message::Event)
.boxed();
let req_stream = futures::stream::unfold(channels.state_rx, |mut rx| async move {
rx.recv().await.map(|req| (req, rx))
})
Expand All @@ -127,23 +126,12 @@ pub async fn run(
.for_each(|msg| {
match msg {
Message::Event(result) => match result {
Ok(signed_event) => {
match proof::update(
&mut app.state,
&mut app.last_height,
sigma_poa,
signed_event,
) {
Ok(_) => {
update_app(&mut app);
println!("New count: {}", app.state.count);
if channels.current_count.send(app.state.count).is_err() {
eprintln!("Failed to send state update");
}
}
Err(e) => {
eprintln!("Failed to update state: {e}");
}
Ok(input) => {
state::update(&mut app.state, input);
update_app(&mut app);
println!("New count: {}", app.state.count);
if channels.current_count.send(app.state.count).is_err() {
eprintln!("Failed to send state update");
}
}
Err(e) => {
Expand Down
46 changes: 1 addition & 45 deletions crates/increment/src/proof.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
//! The proof module contains all the logic that is required to
//! be verified before signing a proof of new state.
use alloy::{
primitives::{Address, Log},
sol_types::SolEvent,
primitives::{Address},
};
use serde::{Deserialize, Serialize};
use sigma_types::{Event, Height, Signed};

use crate::{
Increment::Incremented,
sequence,
signing::verify_signature,
state::{self, State},
};

/// This is the proof that is signed by the apps POA
/// and sent to the underlying chains contract
Expand All @@ -23,38 +14,3 @@ pub struct Proof {
/// The sender address that resulted in this count
pub sender: Address,
}

/// This is the full function that must be run before
/// a proof of the new state can be signed.
pub fn update(
pre_state: &mut State,
last_height: &mut Height,
sigma_poa: &Address,
signed_event: Signed<Event>,
) -> anyhow::Result<()> {
// Verify the sigma POA signature of the event
let event = verify_signature(signed_event, sigma_poa)?;

// Check the height is sequential
if !sequence::check(last_height, &event.height) {
return Err(anyhow::anyhow!(
"Invalid height: expected {}, got {}",
last_height.saturating_add(1),
event.height
));
}

// Update the last height
*last_height = event.height;

// Decode the log
let log: Log = serde_json::from_slice(&event.event_data.data)?;
let event = Incremented::decode_log(&log)
.map_err(|_| anyhow::anyhow!("Failed to decode log"))?
.data;

// Once all the above are verified, we can update the state
state::update(pre_state, event);

Ok(())
}
9 changes: 0 additions & 9 deletions crates/increment/src/sequence.rs

This file was deleted.

37 changes: 3 additions & 34 deletions crates/increment/src/signing.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,13 @@
use alloy::{
hex::FromHex,
primitives::{Address, FixedBytes, keccak256},
signers::{Signature, SignerSync, local::PrivateKeySigner},
primitives::{FixedBytes, keccak256},
signers::{SignerSync, local::PrivateKeySigner},
sol_types::SolValue,
};
use sigma_types::{Event, Signed};
use sigma_types::Signed;

use crate::proof::Proof;

/// Verify the signature of a signed event against the expected POA address.
pub fn verify_signature(signed_event: Signed<Event>, sigma_poa: &Address) -> anyhow::Result<Event> {
// Decode the signature
let Ok(sig) = Signature::try_from(&signed_event.signature[..]) else {
return Err(anyhow::anyhow!("Invalid signature"));
};

// Encode in the same way as the Solidity contract
let encoded = (
signed_event.data.height,
signed_event.data.event_data.stream_type as u64,
&signed_event.data.event_data.data,
)
.abi_encode_packed();

// Hash the encoded data
let h = keccak256(encoded);

// Recover the address from the signature
let addr = sig.recover_address_from_prehash(&h)?;

// Check if the recovered address matches the expected POA address
if addr == *sigma_poa {
Ok(signed_event.data)
} else {
Err(anyhow::anyhow!(
"Signature does not match the expected POA address"
))
}
}

/// Get a signer from an environment variable or generate a random one.
pub fn get_signer(key: Option<String>) -> anyhow::Result<PrivateKeySigner> {
match key {
Expand Down
11 changes: 10 additions & 1 deletion crates/increment/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::HashMap;

use alloy::primitives::Address;
use alloy::{primitives::Address, sol_types::SolEvent};
use sigma_types::Event;

use crate::Increment::Incremented;

Expand All @@ -21,3 +22,11 @@ pub fn update(state: &mut State, event: Incremented) {
// Insert the sender address into the owners map
state.owners.insert(state.count, event.sender);
}

pub fn map_input(event: Event) -> anyhow::Result<Incremented> {
// Decode the event data into an Incremented struct
let log: alloy::primitives::Log = serde_json::from_slice(&event.event_data.data)?;
Incremented::decode_log(&log)
.map_err(|_| anyhow::anyhow!("Failed to decode log"))
.map(|decoded| decoded.data)
}
17 changes: 17 additions & 0 deletions crates/kit/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "kit"
version = "0.1.0"
edition.workspace = true
authors.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true

[dependencies]
alloy.workspace = true
anyhow.workspace = true
futures.workspace = true
reqwest.workspace = true
sigma-stream.workspace = true
sigma-types.workspace = true
tokio.workspace = true
3 changes: 3 additions & 0 deletions crates/kit/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod poa;
pub mod verify;
pub mod state;
37 changes: 37 additions & 0 deletions crates/kit/src/poa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use alloy::{
primitives::{Address, keccak256},
signers::Signature,
sol_types::SolValue,
};
use sigma_types::{Event, Signed};

/// Verify the signature of a signed event against the expected POA address.
pub fn verify_signature(signed_event: Signed<Event>, sigma_poa: &Address) -> anyhow::Result<Event> {
// Decode the signature
let Ok(sig) = Signature::try_from(&signed_event.signature[..]) else {
return Err(anyhow::anyhow!("Invalid signature"));
};

// Encode in the same way as the Solidity contract
let encoded = (
signed_event.data.height,
signed_event.data.event_data.stream_type as u64,
&signed_event.data.event_data.data,
)
.abi_encode_packed();

// Hash the encoded data
let h = keccak256(encoded);

// Recover the address from the signature
let addr = sig.recover_address_from_prehash(&h)?;

// Check if the recovered address matches the expected POA address
if addr == *sigma_poa {
Ok(signed_event.data)
} else {
Err(anyhow::anyhow!(
"Signature does not match the expected POA address"
))
}
}
9 changes: 9 additions & 0 deletions crates/kit/src/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use sigma_types::Event;

pub trait StateUpdate {
type Input;

fn update(&mut self, input: Self::Input);

fn map_input(event: Event) -> anyhow::Result<Self::Input>;
}
Loading
Loading