Skip to content

Commit

Permalink
feat(proposal_create): fail when creating proposal for stale transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
vovacodes committed May 8, 2023
1 parent 9ef383e commit e732411
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 6 deletions.
6 changes: 3 additions & 3 deletions Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ wallet = "~/.config/solana/id.json"
[test.validator]
url = "https://api.devnet.solana.com"

## Token2022
#[[test.validator.clone]]
#address = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
# Token2022
[[test.validator.clone]]
address = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"


[scripts]
Expand Down
6 changes: 6 additions & 0 deletions programs/multisig/src/instructions/proposal_create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ impl ProposalCreate<'_> {
MultisigError::InvalidTransactionIndex
);

// We can't create a proposal for a stale transaction.
require!(
args.transaction_index > multisig.stale_transaction_index,
MultisigError::StaleProposal
);

// Anyone can create a Proposal account. It's similar to ATA in this regard.
// We don't require `Permission::Initiate` here because it's already implicitly checked
// by the fact that a proposal can only be initialized if the corresponding transaction exists,
Expand Down
2 changes: 1 addition & 1 deletion tests/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// The order of imports is the order the test suite will run in.
import "./suites/multisig-sdk";
import "./suites/examples/batch-sol-transfer";
// import "./suites/examples/create-mint";
import "./suites/examples/create-mint";
import "./suites/examples/immediate-execution";
68 changes: 66 additions & 2 deletions tests/suites/multisig-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -817,13 +817,29 @@ describe("Multisig SDK", () => {
timeLock: 0,
})
)[0];

// Create a config transaction.
const newMember = {
key: Keypair.generate().publicKey,
permissions: Permissions.all(),
} as const;

let signature = await multisig.rpc.configTransactionCreate({
connection,
feePayer: members.proposer,
multisigPda,
transactionIndex: 1n,
creator: members.proposer.publicKey,
actions: [{ __kind: "AddMember", newMember }],
});
await connection.confirmTransaction(signature);
});

it("error: invalid transaction index", async () => {
const rentPayer = await generateFundedKeypair(connection);

// Attempt to create a proposal for a transaction that doesn't exist.
const transactionIndex = 1n;
const transactionIndex = 2n;
await assert.rejects(
() =>
multisig.rpc.proposalCreate({
Expand All @@ -840,7 +856,7 @@ describe("Multisig SDK", () => {
it("anyone can create proposals for transactions", async () => {
const nonMember = await generateFundedKeypair(connection);

const transactionIndex = 1n;
const transactionIndex = 2n;

// Create a config transaction.
let signature = await multisig.rpc.configTransactionCreate({
Expand Down Expand Up @@ -889,6 +905,54 @@ describe("Multisig SDK", () => {
assert.deepEqual(proposalAccount.rejected, []);
assert.deepEqual(proposalAccount.cancelled, []);
});

it("error: cannot create proposal for stale transaction", async () => {
// Approve the second config transaction.
let signature = await multisig.rpc.proposalApprove({
connection,
feePayer: members.voter,
multisigPda,
transactionIndex: 2n,
member: members.voter,
});
await connection.confirmTransaction(signature);

signature = await multisig.rpc.proposalApprove({
connection,
feePayer: members.almighty,
multisigPda,
transactionIndex: 2n,
member: members.almighty,
});
await connection.confirmTransaction(signature);

// Execute the second config transaction.
signature = await multisig.rpc.configTransactionExecute({
connection,
feePayer: members.almighty,
multisigPda,
transactionIndex: 2n,
member: members.almighty,
rentPayer: members.almighty,
});
await connection.confirmTransaction(signature);

const feePayer = await generateFundedKeypair(connection);

// At this point the first transaction should become stale.
// Attempt to create a proposal for it should fail.
await assert.rejects(
() =>
multisig.rpc.proposalCreate({
connection,
feePayer,
multisigPda,
transactionIndex: 1n,
rentPayer: feePayer,
}),
/Proposal is stale/
);
});
});

describe("proposal_approve", () => {
Expand Down

0 comments on commit e732411

Please sign in to comment.