Skip to content

Commit 7220bbc

Browse files
authored
BM-261: Added signature validation to orders + unit test (github#104)
This PR adds signature checking for new orders coming from the market events before they are added to the broker DB. Additionally it adds unit tests for signing and validating proof request signatures. closes BM-261
1 parent ee95a51 commit 7220bbc

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

crates/boundless-market/src/contracts/mod.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::str::FromStr;
99
#[cfg(not(target_os = "zkvm"))]
1010
use alloy::{
1111
contract::Error as ContractErr,
12+
primitives::SignatureError,
1213
signers::{Error as SignerErr, Signature, SignerSync},
1314
sol_types::{Error as DecoderErr, SolInterface, SolStruct},
1415
transports::TransportError,
@@ -182,6 +183,25 @@ impl ProvingRequest {
182183
let hash = self.eip712_signing_hash(&domain.alloy_struct());
183184
signer.sign_hash_sync(&hash)
184185
}
186+
187+
/// Verifies the proving request signature with the given signer and EIP-712 domain derived from
188+
/// the given contract address and chain ID.
189+
pub fn verify_signature(
190+
&self,
191+
signature: &Bytes,
192+
contract_addr: Address,
193+
chain_id: u64,
194+
) -> Result<(), SignerErr> {
195+
let sig = Signature::try_from(signature.as_ref())?;
196+
let domain = eip712_domain(contract_addr, chain_id);
197+
let hash = self.eip712_signing_hash(&domain.alloy_struct());
198+
let addr = sig.recover_address_from_prehash(&hash)?;
199+
if addr == self.client_address() {
200+
Ok(())
201+
} else {
202+
Err(SignerErr::SignatureError(SignatureError::FromBytes("Address mismatch")))
203+
}
204+
}
185205
}
186206

187207
impl Requirements {
@@ -587,3 +607,76 @@ pub mod test_utils {
587607
}
588608
}
589609
}
610+
611+
#[cfg(test)]
612+
mod tests {
613+
use super::*;
614+
use alloy::signers::local::PrivateKeySigner;
615+
616+
fn create_order(
617+
signer: &impl SignerSync,
618+
signer_addr: Address,
619+
order_id: u32,
620+
contract_addr: Address,
621+
chain_id: u64,
622+
) -> (ProvingRequest, [u8; 65]) {
623+
let request_id = request_id(&signer_addr, order_id);
624+
625+
let req = ProvingRequest {
626+
id: request_id,
627+
requirements: Requirements {
628+
imageId: B256::ZERO,
629+
predicate: Predicate {
630+
predicateType: PredicateType::PrefixMatch,
631+
data: Default::default(),
632+
},
633+
},
634+
imageUrl: "test".to_string(),
635+
input: Input { inputType: InputType::Url, data: Default::default() },
636+
offer: Offer {
637+
minPrice: U96::from(0),
638+
maxPrice: U96::from(1),
639+
biddingStart: 0,
640+
timeout: 1000,
641+
rampUpPeriod: 1,
642+
lockinStake: U96::from(0),
643+
},
644+
};
645+
646+
let client_sig = req.sign_request(&signer, contract_addr, chain_id).unwrap();
647+
648+
(req, client_sig.as_bytes())
649+
}
650+
651+
#[test]
652+
fn validate_sig() {
653+
let signer: PrivateKeySigner =
654+
"6f142508b4eea641e33cb2a0161221105086a84584c74245ca463a49effea30b".parse().unwrap();
655+
let order_id: u32 = 1;
656+
let contract_addr = Address::ZERO;
657+
let chain_id = 1;
658+
let signer_addr = signer.address();
659+
660+
let (req, client_sig) =
661+
create_order(&signer, signer_addr, order_id, contract_addr, chain_id);
662+
663+
req.verify_signature(&Bytes::from(client_sig), contract_addr, chain_id).unwrap();
664+
}
665+
666+
#[test]
667+
#[should_panic(expected = "SignatureError")]
668+
fn invalid_sig() {
669+
let signer: PrivateKeySigner =
670+
"6f142508b4eea641e33cb2a0161221105086a84584c74245ca463a49effea30b".parse().unwrap();
671+
let order_id: u32 = 1;
672+
let contract_addr = Address::ZERO;
673+
let chain_id = 1;
674+
let signer_addr = signer.address();
675+
676+
let (req, mut client_sig) =
677+
create_order(&signer, signer_addr, order_id, contract_addr, chain_id);
678+
679+
client_sig[0] = 1;
680+
req.verify_signature(&Bytes::from(client_sig), contract_addr, chain_id).unwrap();
681+
}
682+
}

crates/broker/src/market_monitor.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ where
160160
}
161161

162162
async fn monitor_orders(market_addr: Address, provider: Arc<P>, db: DbObj) -> Result<()> {
163+
let chain_id = provider.get_chain_id().await?;
164+
163165
let market = ProofMarketService::new(market_addr, provider, Address::ZERO);
164166
// TODO: RPC providers can drop filters over time or flush them
165167
// we should try and move this to a subscription filter if we have issue with the RPC
@@ -173,6 +175,19 @@ where
173175
match log_res {
174176
Ok((event, _log)) => {
175177
tracing::info!("Detected new request {:x}", event.request.id);
178+
179+
if let Err(err) = event.request.verify_signature(
180+
&event.clientSignature,
181+
market_addr,
182+
chain_id,
183+
) {
184+
tracing::warn!(
185+
"Failed to validate order signature: 0x{:x} - {err:?}",
186+
event.request.id
187+
);
188+
return;
189+
}
190+
176191
if let Err(err) = db
177192
.add_order(
178193
U256::from(event.request.id),

0 commit comments

Comments
 (0)