diff --git a/Cargo.toml b/Cargo.toml index 21200ff..9bb8088 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ description = "EIP-7732 — Enshrined Proposer-Builder Separation (ePBS) referen license = "Apache-2.0" repository = "https://github.com/EIPs-CodeLab/EIP-7732" keywords = ["ethereum", "eip-7732", "epbs", "consensus", "builder"] +# PR #1: BLS signing roots and verification wiring [[bin]] name = "epbs-cli" diff --git a/src/beacon_chain/process_payload_attestation.rs b/src/beacon_chain/process_payload_attestation.rs index e3ef2b8..4044397 100644 --- a/src/beacon_chain/process_payload_attestation.rs +++ b/src/beacon_chain/process_payload_attestation.rs @@ -104,7 +104,7 @@ fn verify_aggregate_ptc_signature( pubkeys: &[BLSPubkey], ) -> Result<(), PayloadAttestationError> { let domain = ssz::compute_domain_simple(DOMAIN_PTC_ATTESTER); - let signing_root = ssz::signing_root(data, domain); + let signing_root = ssz::signing_root_json(data, domain); crypto::bls_verify_aggregate(pubkeys, &signing_root, signature) .map_err(|_| PayloadAttestationError::InvalidSignature) } diff --git a/src/beacon_chain/process_payload_bid.rs b/src/beacon_chain/process_payload_bid.rs index 324295a..fef6e01 100644 --- a/src/beacon_chain/process_payload_bid.rs +++ b/src/beacon_chain/process_payload_bid.rs @@ -117,7 +117,7 @@ fn verify_builder_signature( .ok_or(PayloadBidError::MissingPubkey(message.builder_index))?; let domain = ssz::compute_domain_simple(DOMAIN_BEACON_BUILDER); - let signing_root = ssz::signing_root(message, domain); + let signing_root = ssz::signing_root_json(message, domain); crypto::bls_verify(&pk, &signing_root, &signed_bid.signature) .map_err(|_| PayloadBidError::InvalidSignature) } diff --git a/src/builder/bid.rs b/src/builder/bid.rs index cd0ceff..61d37a4 100644 --- a/src/builder/bid.rs +++ b/src/builder/bid.rs @@ -91,7 +91,7 @@ pub fn construct_bid( }; let domain = ssz::compute_domain_simple(DOMAIN_BEACON_BUILDER); - let signing_root = ssz::signing_root(&message, domain); + let signing_root = ssz::signing_root_json(&message, domain); let signature = sign_fn(&signing_root).map_err(BidError::SigningFailed)?; diff --git a/src/builder/envelope.rs b/src/builder/envelope.rs index c6264fd..047d521 100644 --- a/src/builder/envelope.rs +++ b/src/builder/envelope.rs @@ -84,7 +84,7 @@ pub fn construct_envelope( }; let domain = ssz::compute_domain_simple(DOMAIN_BEACON_BUILDER); - let signing_root = ssz::signing_root(&message, domain); + let signing_root = ssz::signing_root_json(&message, domain); let signature = sign_fn(&signing_root).map_err(EnvelopeError::SigningFailed)?; Ok(SignedExecutionPayloadEnvelope { message, signature }) diff --git a/src/utils/ssz.rs b/src/utils/ssz.rs index b557c8b..ef65306 100644 --- a/src/utils/ssz.rs +++ b/src/utils/ssz.rs @@ -10,12 +10,19 @@ pub fn compute_domain_simple(domain_type: [u8; 4]) -> [u8; 32] { domain } -/// Compute a signing root by hashing serialized message bytes plus domain. -/// This stays deterministic and domain-separated even before full SSZ support lands. -pub fn signing_root(message: &T, domain: [u8; 32]) -> [u8; 32] { - let encoded = serde_json::to_vec(message).expect("serialize message for signing"); +/// Minimal hash_tree_root stand-in: SHA256 over JSON serialization of the message. +/// This keeps signing deterministic and domain-separated until full SSZ is wired. +pub fn hash_tree_root_json(value: &T) -> [u8; 32] { + let encoded = serde_json::to_vec(value).expect("serialize message"); let mut hasher = Sha256::new(); hasher.update(encoded); + hasher.finalize().into() +} + +/// signing_root = hash_tree_root(message) mixed with domain. +pub fn signing_root_json(message: &T, domain: [u8; 32]) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(hash_tree_root_json(message)); hasher.update(domain); hasher.finalize().into() } diff --git a/tests/unit/beacon_chain_test.rs b/tests/unit/beacon_chain_test.rs index 8049e99..0409063 100644 --- a/tests/unit/beacon_chain_test.rs +++ b/tests/unit/beacon_chain_test.rs @@ -99,7 +99,7 @@ fn make_valid_bid(slot: Slot) -> SignedExecutionPayloadBid { blob_kzg_commitments: vec![], }; let domain = ssz::compute_domain_simple(DOMAIN_BEACON_BUILDER); - let signing_root = ssz::signing_root(&message, domain); + let signing_root = ssz::signing_root_json(&message, domain); SignedExecutionPayloadBid { message, signature: crypto::bls_sign(&test_secret_key(), &signing_root), @@ -214,7 +214,7 @@ fn valid_ptc_attestation_accepted() { blob_data_available: true, }; let domain = ssz::compute_domain_simple(DOMAIN_PTC_ATTESTER); - let signing_root = ssz::signing_root(&data, domain); + let signing_root = ssz::signing_root_json(&data, domain); let signature = aggregate_signature(&aggregation_bits, &signing_root); let att = PayloadAttestation { aggregation_bits,