Background
bft_consensus.rs::create_proposal builds and stores a cross-chain proposal but never records which relayer/caller submitted it:
/// # TODO
/// - Add a proposer field so off-chain indexers can attribute proposals
/// to specific relayers for analytics and accountability.
pub fn create_proposal(env: &Env, message: CrossChainMessage) -> Result<u64, BridgeError> {
...
}
Without a stored proposer, the indexer (indexer/) and any off-chain analytics/reputation system have no on-chain way to attribute a proposal to the relayer that submitted it. This blocks per-relayer accountability (e.g., penalizing relayers who repeatedly submit invalid/expiring proposals) and per-relayer analytics on the observability dashboards already built for issue #321.
Implementation Plan
- Add a
proposer: Address field to the proposal struct, captured via env.current_contract_address()/caller identity at creation time in create_proposal, with require_auth() on the proposer.
- Emit the proposer in the proposal-created event so the indexer can pick it up without an extra storage read.
- Update indexer models/queries (if applicable) to surface proposer in proposal analytics.
- Add a test confirming the stored proposal and emitted event both carry the correct proposer address.
Acceptance Criteria
- Every created proposal records a
proposer address
- The proposer is authenticated (
require_auth) at creation time
- Proposal-created events include the proposer
- A test confirms proposer attribution end-to-end
Background
bft_consensus.rs::create_proposalbuilds and stores a cross-chain proposal but never records which relayer/caller submitted it:Without a stored proposer, the indexer (
indexer/) and any off-chain analytics/reputation system have no on-chain way to attribute a proposal to the relayer that submitted it. This blocks per-relayer accountability (e.g., penalizing relayers who repeatedly submit invalid/expiring proposals) and per-relayer analytics on the observability dashboards already built for issue #321.Implementation Plan
proposer: Addressfield to the proposal struct, captured viaenv.current_contract_address()/caller identity at creation time increate_proposal, withrequire_auth()on the proposer.Acceptance Criteria
proposeraddressrequire_auth) at creation time