fix(dao): prepend (cons) Created lock instead of append in proposal_create
Agora's stake validator (ppermitVote) enforces output_locks = pcons NEW_LOCK old_locks (head-cons, not append). proposal_create was using Vec::push which appends, so when the stake had any pre-existing locks the output lock order didn't match what the validator expected and the chain rejected with CekError on the stake validator (5178-byte script, hash 57d6b17f...). The bug went undetected on 2026-05-07 because that day's first proposal_create ran on a stake with locked_by = [], where push and prepend produce the same single-element vector. Today's proposal #1 attempt — stake already holding a Created lock for #0 — flushed it out. Mirror the prepend pattern that proposal_cosign and proposal_vote already use: build new_locks with the new lock at index 0, then extend with the old locks.
This commit is contained in:
parent
5e6cb7056b
commit
b7074fd81b
1 changed files with 12 additions and 3 deletions
|
|
@ -352,12 +352,21 @@ pub fn build_unsigned_proposal_create(
|
|||
starting_time: args.starting_time_ms,
|
||||
};
|
||||
|
||||
// New stake datum: copy old, append Created lock for the new proposal.
|
||||
let mut new_stake = args.stake_in.datum.clone();
|
||||
new_stake.locked_by.push(ProposalLock {
|
||||
// New stake datum: copy old, PREPEND a Created lock for the new
|
||||
// proposal. Order matters — the stake validator's ppermitVote uses
|
||||
// `pcons NEW_LOCK old_locks` (head-cons, NOT append). If we append
|
||||
// and the input had pre-existing locks, the chain rejects with
|
||||
// CekError on the stake validator. Caught 2026-05-08 trying to
|
||||
// create proposal #1 while the stake still held a Created lock
|
||||
// from proposal #0; cosign + vote builders already prepend.
|
||||
let mut new_locks = Vec::with_capacity(args.stake_in.datum.locked_by.len() + 1);
|
||||
new_locks.push(ProposalLock {
|
||||
proposal_id: new_proposal_id,
|
||||
action: ProposalAction::Created,
|
||||
});
|
||||
new_locks.extend(args.stake_in.datum.locked_by.iter().cloned());
|
||||
let mut new_stake = args.stake_in.datum.clone();
|
||||
new_stake.locked_by = new_locks;
|
||||
|
||||
let new_governor_datum_pd = new_governor.to_plutus_data()?;
|
||||
let new_proposal_datum_pd = new_proposal.to_plutus_data()?;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue