Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2e59dc0
feat(contract): report pending parameter votes by proposal hash in st…
SimonRastikian Oct 20, 2026
4adcd90
test(contract): update the ABI snapshot for the parameter-votes tally
SimonRastikian Oct 20, 2026
69286e4
fix(contract-interface): accept the legacy parameter-votes report whe…
SimonRastikian Oct 21, 2026
ccd5f10
Merge branch '2825-store-parameter-votes-hash-only' into 2825-report-…
SimonRastikian Oct 22, 2026
0c780d9
test(contract): use the Environment helper in the re-proposal state()…
SimonRastikian Oct 22, 2026
46a0897
Merge branch '2825-store-parameter-votes-hash-only' into 2825-report-…
SimonRastikian Oct 22, 2026
607cbaa
Merge branch '2825-store-parameter-votes-hash-only' into 2825-report-…
SimonRastikian Oct 22, 2026
14ec286
docs(contract-interface): track removal of the legacy parameter-votes…
SimonRastikian Oct 23, 2026
90e7148
Copilot Nits
SimonRastikian Oct 23, 2026
0e1fe84
test(contract): accept the ABI snapshot and drop the stray .snap.new
SimonRastikian Oct 23, 2026
fbded4f
Merge branch '2825-store-parameter-votes-hash-only' into 2825-report-…
SimonRastikian Oct 23, 2026
8421b40
docs(contract-interface): point the legacy parameter-votes arm at its…
SimonRastikian Oct 23, 2026
92b7184
docs(contract-interface): note the end state when dropping the legacy…
SimonRastikian Oct 23, 2026
9968b6f
Merge branch '2825-store-parameter-votes-hash-only' into 2825-report-…
SimonRastikian Oct 23, 2026
ae2479c
docs(contract-interface): drop the paraphrase from the previous_runni…
SimonRastikian Oct 26, 2026
e6e95bb
docs(contract-interface): drop the previous_running_state doc
SimonRastikian Oct 26, 2026
cf6a6f2
test(contract-interface): cover a populated legacy parameter-votes re…
SimonRastikian Oct 26, 2026
958f611
refactor(contract): expose pending parameter votes via new_parameters…
SimonRastikian Oct 26, 2026
0afd1e8
test(contract): update the ABI snapshot for the new_parameters_votes …
SimonRastikian Oct 26, 2026
d144e7f
test(contract-interface): pin legacy parameters_votes tolerance and d…
SimonRastikian Oct 27, 2026
aea3371
Merge branch '2825-store-parameter-votes-hash-only' into 2825-report-…
SimonRastikian Oct 27, 2026
1a40b34
test(contract-interface): use DomainId for next_domain_id in the lega…
SimonRastikian Oct 27, 2026
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
3 changes: 0 additions & 3 deletions crates/backup-cli/assets/contract_state.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,6 @@
},
"threshold": 7
},
"parameters_votes": {
"proposal_by_account": {}
},
"add_domains_votes": {
"proposal_by_account": {}
}
Expand Down
1 change: 1 addition & 0 deletions crates/contract/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ These functions require the caller to be a participant or candidate.
| `allowed_os_measurements()` | Returns all currently allowed OS measurement sets. | `Vec<ExpectedMeasurements>` | TBD | TBD |
| `os_measurement_votes()` | Returns pending OS measurement votes, keyed by the proposal hash of the action voted for, with the accounts backing each; may include former participants' votes. | `MeasurementVotes` | TBD | TBD |
| `contract_update_votes()` | Returns current update votes, keyed by the hash voted for, with the accounts backing each. | `BTreeMap<ProposalHash, BTreeSet<AccountId>>` | TBD | TBD |
| `new_parameters_votes()` | Returns pending `vote_new_parameters` votes, keyed by the proposal hash of the `ProposedGovernanceThresholdParameters` voted for, with the accounts backing each. | `GovernanceThresholdParametersVotes` | TBD | TBD |
| `clean_tee_status()` | Private endpoint. Cleans up TEE information for non-participants after resharing. Only callable by the contract itself via a promise. | `Result<(), Error>` | TBD | TBD |

## Building the contract
Expand Down
65 changes: 63 additions & 2 deletions crates/contract/src/api/governance.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Governance votes over the participant set and the domain registry.

use crate::dto_mapping::TryIntoContractType;
use crate::dto_mapping::{IntoInterfaceType, TryIntoContractType};
use crate::errors::{Error, InvalidParameters};
use crate::primitives::thresholds::{GovernanceThreshold, ProposedGovernanceThresholdParameters};
use crate::tee::tee_state::TeeValidationResult;
Expand Down Expand Up @@ -104,6 +104,11 @@ impl MpcContract {
}
Ok(())
}

/// Returns the pending `vote_new_parameters` votes, keyed by proposal hash.
pub fn new_parameters_votes(&self) -> dtos::GovernanceThresholdParametersVotes {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just parameter_votes? I don't think the new_* prefix adds anything of value. In fact, I think it makes it more confusing.

(&self.parameters_votes).into_dto_type()
}
}

impl MpcContract {
Expand All @@ -121,10 +126,10 @@ mod tests {
forwarded_participant_call_contract, make_public_key_for_curve, setup_tee_test_contract,
submit_attestation, submit_valid_attestations,
};
use crate::dto_mapping::IntoInterfaceType;
use crate::errors::{DomainError, InvalidCandidateSet, InvalidState, InvalidThreshold};
use crate::primitives::key_state::{AttemptId, EpochId, KeyEventId, KeyForDomain, Keyset};
use crate::primitives::participants::Participants;
use crate::primitives::proposal_hash::ToProposalHash;
use crate::primitives::test_utils::{
authenticate_account_as, bogus_tee_verifier_account_id, gen_participants,
};
Expand Down Expand Up @@ -571,6 +576,62 @@ mod tests {
(contract, participants, domain_id, proposal)
}

/// The tally for `voters` all voting `proposal`.
fn expected_tally(
proposal: &ProposedGovernanceThresholdParameters,
voters: &[&AccountId],
) -> dtos::GovernanceThresholdParametersVotes {
dtos::GovernanceThresholdParametersVotes(BTreeMap::from([(
proposal.to_proposal_hash(),
voters
.iter()
.map(|voter| dtos::AuthenticatedAccountId((*voter).clone()))
.collect(),
)]))
}

#[test]
fn new_parameters_votes__should_report_pending_votes_by_proposal_hash() {
// Given
let (mut contract, participants, signer, _) = setup_running_contract_with_domain(
3,
GovernanceThreshold::new(2),
ReconstructionThreshold::new(2),
);
let proposal = ProposedGovernanceThresholdParameters::new(
GovernanceThresholdParameters::new(participants, GovernanceThreshold::new(2)).unwrap(),
BTreeMap::new(),
);

// When
vote_params(&mut contract, &signer, &proposal).unwrap();

// Then
assert_eq!(
contract.new_parameters_votes(),
expected_tally(&proposal, &[&signer])
);
}

#[test]
fn new_parameters_votes__should_report_re_proposal_votes_while_resharing() {
// Given
let (mut contract, participants, _, proposal) = resharing_contract_with_domain();
let (voter, _, _) = &participants.participants()[0];
Environment::new(Some(0), Some(voter.clone()), None);

// When
contract
.vote_new_parameters(dtos::EpochId::new(2), (&proposal).into_dto_type())
.unwrap();

// Then
assert_eq!(
contract.new_parameters_votes(),
expected_tally(&proposal, &[voter])
);
}

#[test]
fn vote_new_parameters__should_record_a_re_proposal_vote_while_resharing() {
// Given
Expand Down
20 changes: 16 additions & 4 deletions crates/contract/src/dto_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use mpc_attestation::{
collateral::Collateral,
};
use near_mpc_contract_interface::types as dtos;
use std::collections::BTreeMap;

use crate::{
config::Config,
Expand All @@ -24,6 +23,7 @@ use crate::{
domain::{AddDomainsVotes, DomainRegistry},
key_state::{AuthenticatedAccountId, AuthenticatedParticipantId, KeyForDomain, Keyset},
participants::{ParticipantInfo, Participants},
threshold_votes::GovernanceThresholdParametersVotes,
thresholds::{GovernanceThresholdParameters, ProposedGovernanceThresholdParameters},
votes::Votes,
},
Expand Down Expand Up @@ -816,6 +816,21 @@ impl IntoInterfaceType<dtos::AddDomainsVotes> for &AddDomainsVotes {
}
}

impl IntoInterfaceType<dtos::GovernanceThresholdParametersVotes>
for &GovernanceThresholdParametersVotes
{
fn into_dto_type(self) -> dtos::GovernanceThresholdParametersVotes {
dtos::GovernanceThresholdParametersVotes(
self.all()
.into_iter()
.map(|(proposal, voters)| {
(proposal, voters.iter().map(|v| v.into_dto_type()).collect())
})
.collect(),
)
}
}

impl IntoInterfaceType<dtos::MeasurementVotes> for &Votes<AuthenticatedAccountId> {
fn into_dto_type(self) -> dtos::MeasurementVotes {
dtos::MeasurementVotes(
Expand Down Expand Up @@ -964,9 +979,6 @@ impl IntoInterfaceType<dtos::RunningContractState> for &RunningContractState {
domains: (&self.domains).into_dto_type(),
keyset: (&self.keyset).into_dto_type(),
parameters: (&self.parameters).into_dto_type(),
parameters_votes: dtos::GovernanceThresholdParametersVotes {
proposal_by_account: BTreeMap::new(),
},
add_domains_votes: (&self.add_domains_votes).into_dto_type(),
previously_cancelled_resharing_epoch_id: self.previously_cancelled_resharing_epoch_id,
}
Expand Down
7 changes: 6 additions & 1 deletion crates/contract/src/primitives/threshold_votes.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::primitives::proposal_hash::{Borsh, Sha256, ToProposalHash};
use crate::primitives::proposal_hash::{Borsh, ProposalHash, Sha256, ToProposalHash};
use crate::primitives::thresholds::ProposedGovernanceThresholdParameters;
use crate::primitives::votes::Votes;
use crate::primitives::{
Expand All @@ -7,6 +7,7 @@ use crate::primitives::{
};
use crate::storage_keys::StorageKey;
use near_sdk::near;
use std::collections::{BTreeMap, BTreeSet};

impl ToProposalHash for ProposedGovernanceThresholdParameters {
type Serializer = Borsh;
Expand Down Expand Up @@ -65,6 +66,10 @@ impl GovernanceThresholdParametersVotes {
self.0.clear();
}

pub fn all(&self) -> BTreeMap<ProposalHash, BTreeSet<AuthenticatedAccountId>> {
self.0.all()
}

#[cfg(test)]
pub fn is_empty(&self) -> bool {
self.0.all().is_empty()
Expand Down
39 changes: 19 additions & 20 deletions crates/contract/tests/snapshots/abi__abi_has_not_changed.snap
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,24 @@ expression: abi
}
}
},
{
"name": "new_parameters_votes",
"doc": " Returns the pending `vote_new_parameters` votes, keyed by proposal hash.",
"kind": "view",
"result": {
"serialization_type": "json",
"type_schema": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"$ref": "#/definitions/AuthenticatedAccountId"
},
"uniqueItems": true
}
}
}
},
{
"name": "os_measurement_votes",
"doc": " Returns pending OS measurement votes, may include former participants' votes",
Expand Down Expand Up @@ -3716,21 +3734,6 @@ expression: abi
}
}
},
"GovernanceThresholdParametersVotes": {
"description": "Votes for threshold parameter changes. Always empty: the contract stores only proposal hashes and no longer reports pending votes. Kept for wire compatibility.",
"type": "object",
"required": [
"proposal_by_account"
],
"properties": {
"proposal_by_account": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/ProposedGovernanceThresholdParameters"
}
}
}
},
"Hash256": {
"type": "string",
"pattern": "^(?:[0-9A-Fa-f]{2})*$"
Expand Down Expand Up @@ -4765,8 +4768,7 @@ expression: abi
"add_domains_votes",
"domains",
"keyset",
"parameters",
"parameters_votes"
"parameters"
],
"properties": {
"add_domains_votes": {
Expand All @@ -4781,9 +4783,6 @@ expression: abi
"parameters": {
"$ref": "#/definitions/GovernanceThresholdParameters"
},
"parameters_votes": {
"$ref": "#/definitions/GovernanceThresholdParametersVotes"
},
"previously_cancelled_resharing_epoch_id": {
"anyOf": [
{
Expand Down
20 changes: 12 additions & 8 deletions crates/near-mpc-contract-interface/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ use crate::method_names::{
GET_FOREIGN_CHAINS_CONFIGS, GET_PENDING_CKD_REQUEST, GET_PENDING_REQUEST,
GET_PENDING_VERIFY_FOREIGN_TX_REQUEST, GET_TEE_ACCOUNTS, INIT, INIT_RUNNING,
LAUNCHER_HASH_VOTES, MIGRATE, MIGRATION_INFO, MPC_NODE_MANIFEST_DIGEST_VOTES,
OS_MEASUREMENT_VOTES, PREPAY_ATTESTATION_STORAGE, PUBLIC_KEY, REGISTER_BACKUP_SERVICE,
REGISTER_FOREIGN_CHAINS_CONFIG, REMOVE_CONTRACT_UPDATE_VOTE,
NEW_PARAMETERS_VOTES, OS_MEASUREMENT_VOTES, PREPAY_ATTESTATION_STORAGE, PUBLIC_KEY,
REGISTER_BACKUP_SERVICE, REGISTER_FOREIGN_CHAINS_CONFIG, REMOVE_CONTRACT_UPDATE_VOTE,
REMOVE_NON_PARTICIPANT_CONTRACT_UPDATE_VOTES, REQUEST_APP_PRIVATE_KEY, RESPOND, RESPOND_CKD,
RESPOND_VERIFY_FOREIGN_TX, SIGN, START_KEYGEN_INSTANCE, START_NODE_MIGRATION,
START_RESHARE_INSTANCE, STATE, SUBMIT_CONTRACT_UPDATE, SUBMIT_PARTICIPANT_INFO,
Expand All @@ -50,12 +50,12 @@ use crate::types::{
AvailableForeignChains, BackupServiceInfo, CKDAppPublicKey, CKDRequest, CKDRequestArgs,
CKDResponse, ChainEntry, CodeHashesVotes, Config, DestinationNodeInfo, DomainConfig, DomainId,
Ed25519PublicKey, EpochId, ExpectedMeasurements, ForeignChain, ForeignChainsConfig,
ForeignChainsConfigs, GovernanceThresholdParameters, InitConfig, KeyEventId, Keyset,
LauncherDockerComposeHash, LauncherHashVotes, LauncherImageHash, MeasurementVotes,
MigrationInfo, NodeId, NodeImageHash, ParticipantUrl, ProposalHash,
ProposedGovernanceThresholdParameters, ProtocolContractState, PublicKey, SignRequestArgs,
SignatureRequest, SignatureResponse, StoredAttestation, TeeVerifierCodeHash, Update,
UpdateHash, VerifyForeignTransactionRequest, VerifyForeignTransactionRequestArgs,
ForeignChainsConfigs, GovernanceThresholdParameters, GovernanceThresholdParametersVotes,
InitConfig, KeyEventId, Keyset, LauncherDockerComposeHash, LauncherHashVotes,
LauncherImageHash, MeasurementVotes, MigrationInfo, NodeId, NodeImageHash, ParticipantUrl,
ProposalHash, ProposedGovernanceThresholdParameters, ProtocolContractState, PublicKey,
SignRequestArgs, SignatureRequest, SignatureResponse, StoredAttestation, TeeVerifierCodeHash,
Update, UpdateHash, VerifyForeignTransactionRequest, VerifyForeignTransactionRequestArgs,
VerifyForeignTransactionResponse, YieldIndex,
};
use near_mpc_bounded_collections::NonEmptyBTreeMap;
Expand Down Expand Up @@ -633,6 +633,10 @@ impl<C: ViewContract + Clone> MpcContractHandle<C> {
self.view(ViewArgs::no_args(LAUNCHER_HASH_VOTES))
}

pub fn new_parameters_votes(&self) -> ViewCall<C, GovernanceThresholdParametersVotes> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the method should be added to the catalog test below

self.view(ViewArgs::no_args(NEW_PARAMETERS_VOTES))
}

pub fn os_measurement_votes(&self) -> ViewCall<C, MeasurementVotes> {
self.view(ViewArgs::no_args(OS_MEASUREMENT_VOTES))
}
Expand Down
1 change: 1 addition & 0 deletions crates/near-mpc-contract-interface/src/method_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pub const GET_TEE_ACCOUNTS: &str = "get_tee_accounts";
pub const AVAILABLE_ATTESTATION_GRANTS: &str = "available_attestation_grants";
pub const TEE_VERIFIER_ACCOUNT_ID: &str = "tee_verifier_account_id";
pub const TEE_VERIFIER_VOTES: &str = "tee_verifier_votes";
pub const NEW_PARAMETERS_VOTES: &str = "new_parameters_votes";
pub const GET_ATTESTATION: &str = "get_attestation";
pub const GET_AVAILABLE_FOREIGN_CHAINS: &str = "get_available_foreign_chains";
pub const GET_FOREIGN_CHAINS_CONFIGS: &str = "get_foreign_chains_configs";
Expand Down
Loading
Loading