Wire HDHomeRun observations and recover Forge OP Stack
This commit is contained in:
parent
8065860449
commit
0d86104762
18 changed files with 1613 additions and 58 deletions
|
|
@ -17,6 +17,8 @@ pub const SCHEME_MANIFEST_DATA_ROOT: &str = "manifest-data-merkle-keccak256-v1";
|
|||
pub const SCHEME_MANIFEST_BODY_ABI: &str = "manifest-body-abi-keccak256-v1";
|
||||
pub const SCHEME_MANIFEST_ENVELOPE_ABI: &str = "manifest-envelope-abi-keccak256-v1";
|
||||
pub const ETH_MANIFEST_SIG_ALG: &str = "secp256k1-eip712-manifest-body-v1";
|
||||
pub const ZERO_B256_HEX: &str =
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
sol! {
|
||||
struct EthStreamMetadata {
|
||||
|
|
@ -117,6 +119,16 @@ sol! {
|
|||
bytes32 manifestId;
|
||||
EthManifestSignature[] signatures;
|
||||
}
|
||||
|
||||
struct EthObservationHeader {
|
||||
bytes32 streamHash;
|
||||
bytes32 epochHash;
|
||||
bytes32 parentObservationHash;
|
||||
bytes32 dataRoot;
|
||||
bytes32 locatorHash;
|
||||
uint64 observedUnixMs;
|
||||
uint64 sequence;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -142,7 +154,7 @@ fn commitment(scheme: &str, digest: B256) -> ChainCommitment {
|
|||
ChainCommitment {
|
||||
chain: ETHEREUM_CHAIN.to_string(),
|
||||
scheme: scheme.to_string(),
|
||||
digest: format!("0x{}", hex::encode(digest)),
|
||||
digest: b256_hex(digest),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +162,15 @@ fn abi_commitment<T: SolValue>(scheme: &str, value: &T) -> ChainCommitment {
|
|||
commitment(scheme, keccak256(value.abi_encode()))
|
||||
}
|
||||
|
||||
fn parse_b256(value: &str) -> Result<B256, EthCommitmentError> {
|
||||
pub fn b256_hex(value: B256) -> String {
|
||||
format!("0x{}", hex::encode(value))
|
||||
}
|
||||
|
||||
pub fn keccak256_bytes_hex(value: &[u8]) -> String {
|
||||
b256_hex(keccak256(value))
|
||||
}
|
||||
|
||||
pub fn parse_b256(value: &str) -> Result<B256, EthCommitmentError> {
|
||||
let trimmed = value.trim().strip_prefix("0x").unwrap_or(value.trim());
|
||||
let bytes =
|
||||
hex::decode(trimmed).map_err(|_| EthCommitmentError::InvalidHex(value.to_string()))?;
|
||||
|
|
@ -540,6 +560,49 @@ pub fn manifest_commitments(value: &Manifest) -> Result<Vec<ChainCommitment>, Et
|
|||
])
|
||||
}
|
||||
|
||||
pub fn manifest_commitment_digest(
|
||||
value: &Manifest,
|
||||
scheme: &str,
|
||||
) -> Result<Option<String>, EthCommitmentError> {
|
||||
Ok(manifest_commitments(value)?
|
||||
.into_iter()
|
||||
.find(|commitment| commitment.scheme == scheme)
|
||||
.map(|commitment| commitment.digest))
|
||||
}
|
||||
|
||||
pub fn manifest_observation_header(
|
||||
value: &Manifest,
|
||||
parent_observation_hash: Option<&str>,
|
||||
locator_hash: &str,
|
||||
sequence: u64,
|
||||
) -> Result<EthObservationHeader, EthCommitmentError> {
|
||||
let data_root = manifest_commitment_digest(value, SCHEME_MANIFEST_DATA_ROOT)?
|
||||
.ok_or(EthCommitmentError::Empty)?;
|
||||
|
||||
Ok(EthObservationHeader {
|
||||
streamHash: keccak256(value.body.stream_id.0.as_bytes()),
|
||||
epochHash: keccak256(value.body.epoch_id.as_bytes()),
|
||||
parentObservationHash: parse_b256(parent_observation_hash.unwrap_or(ZERO_B256_HEX))?,
|
||||
dataRoot: parse_b256(&data_root)?,
|
||||
locatorHash: parse_b256(locator_hash)?,
|
||||
observedUnixMs: value.body.created_unix_ms,
|
||||
sequence,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn observation_header_hash(value: &EthObservationHeader) -> String {
|
||||
b256_hex(keccak256(value.abi_encode()))
|
||||
}
|
||||
|
||||
pub fn observation_slot_hash(
|
||||
stream_hash: &str,
|
||||
epoch_hash: &str,
|
||||
) -> Result<String, EthCommitmentError> {
|
||||
let stream_hash = parse_b256(stream_hash)?;
|
||||
let epoch_hash = parse_b256(epoch_hash)?;
|
||||
Ok(b256_hex(keccak256((stream_hash, epoch_hash).abi_encode())))
|
||||
}
|
||||
|
||||
pub fn manifest_commitments_match(value: &Manifest) -> Result<bool, EthCommitmentError> {
|
||||
let present = value
|
||||
.commitments
|
||||
|
|
@ -621,6 +684,37 @@ mod tests {
|
|||
assert_eq!(h1, h2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_observation_header_uses_manifest_data_root() {
|
||||
let body = sample_body();
|
||||
let manifest_id = body.manifest_id().unwrap();
|
||||
let mut manifest = Manifest {
|
||||
body,
|
||||
manifest_id,
|
||||
signatures: Vec::new(),
|
||||
commitments: Vec::new(),
|
||||
};
|
||||
manifest.commitments = manifest_commitments(&manifest).unwrap();
|
||||
|
||||
let locator_hash = keccak256_bytes_hex(b"locator");
|
||||
let header = manifest_observation_header(&manifest, None, &locator_hash, 7).unwrap();
|
||||
assert_eq!(header.sequence, 7);
|
||||
assert_eq!(header.observedUnixMs, manifest.body.created_unix_ms);
|
||||
assert_eq!(
|
||||
b256_hex(header.dataRoot),
|
||||
manifest_commitment_digest(&manifest, SCHEME_MANIFEST_DATA_ROOT)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
observation_slot_hash(&b256_hex(header.streamHash), &b256_hex(header.epochHash))
|
||||
.unwrap(),
|
||||
b256_hex(keccak256(
|
||||
(header.streamHash, header.epochHash).abi_encode()
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_descriptor_commitments_include_stream_id_and_descriptor_hashes() {
|
||||
let descriptor = StreamDescriptor {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue