Skip to content
Merged
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
17 changes: 17 additions & 0 deletions contracts/governance/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ pub mod governance {
pub proposer: AccountId,
}

/// Emitted when a signer registers (or overwrites) their ECDSA public key.
#[ink(event)]
pub struct PublicKeyRegistered {
#[ink(topic)]
pub signer: AccountId,
pub public_key: [u8; 33],
pub timestamp: u64,
}

// =========================================================================
// Storage
// =========================================================================
Expand Down Expand Up @@ -575,11 +584,19 @@ pub mod governance {
}

/// Register an ECDSA public key for cryptographic signature verification.
///
/// Emits a `PublicKeyRegistered` event (also on overwrite) so that signer
/// key rotations remain observable to auditors and off-chain indexers.
#[ink(message)]
pub fn register_public_key(&mut self, public_key: [u8; 33]) -> Result<(), Error> {
let caller = self.env().caller();
self.ensure_signer(caller)?;
self.signer_public_keys.insert(caller, &public_key);
self.env().emit_event(PublicKeyRegistered {
signer: caller,
public_key,
timestamp: self.env().block_timestamp(),
});
Ok(())
}

Expand Down
32 changes: 32 additions & 0 deletions contracts/governance/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,38 @@ mod tests {
);
}

#[ink::test]
fn register_public_key_emits_public_key_registered_event() {
use scale::Decode as _;

let mut gov = create_governance();
let accounts = default_accounts();
ink::env::test::set_block_timestamp::<ink::env::DefaultEnvironment>(42);
set_caller(accounts.alice);

gov.register_public_key([0x03; 33]).unwrap();

let events = ink::env::test::recorded_events().collect::<Vec<_>>();
let decoded =
PublicKeyRegistered::decode(&mut &events[0].data[..]).expect("decode event");
assert_eq!(decoded.signer, accounts.alice);
assert_eq!(decoded.public_key, [0x03; 33]);
assert_eq!(decoded.timestamp, 42);

// Overwriting an existing key is a rotation and must also be traceable.
ink::env::test::set_block_timestamp::<ink::env::DefaultEnvironment>(99);
set_caller(accounts.bob);
gov.register_public_key([0x04; 33]).unwrap();
gov.register_public_key([0x05; 33]).unwrap();

let events = ink::env::test::recorded_events().collect::<Vec<_>>();
assert_eq!(events.len(), 3);
let decoded = PublicKeyRegistered::decode(&mut &events[2].data[..]).expect("decode event");
assert_eq!(decoded.signer, accounts.bob);
assert_eq!(decoded.public_key, [0x05; 33]);
assert_eq!(decoded.timestamp, 99);
}

/// Brute-force recount helper mirroring the pre-#972 scan semantics:
/// tallies each proposal by its *final* status and computes the
/// participation average over Executed/Rejected proposals.
Expand Down
Loading