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
5 changes: 5 additions & 0 deletions .changeset/moody-needles-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@solana/instruction-plans': patch
---

Fix a bug where a message packer that requires multiple iterations is not correctly added when forced to fit in a single transaction, even if it can fit
28 changes: 28 additions & 0 deletions packages/instruction-plans/src/__tests__/__setup__.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,34 @@ import { MessagePackerInstructionPlan } from '../instruction-plan';

const MINIMUM_INSTRUCTION_SIZE = 35;

/**
* Creates a message packer that packs one instruction at a time,
* even when there's space to pack more in a single iteration.
* This is useful for testing that the message packer loop correctly accumulates
* results across iterations.
*/
export function createSingleInstructionAtATimeMessagePackerInstructionPlan(
instructions: Instruction[],
): MessagePackerInstructionPlan {
return Object.freeze({
getMessagePacker: () => {
let index = 0;
return {
done: () => index >= instructions.length,
packMessageToCapacity: message => {
if (index >= instructions.length) {
return message;
}
const instruction = instructions[index];
index++;
return appendTransactionMessageInstruction(instruction, message);
},
};
},
kind: 'messagePacker',
});
}

export const FOREVER_PROMISE = new Promise(() => {
/* never resolve */
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { createTransactionPlanner, TransactionPlanner } from '../transaction-pla
import {
createMessage,
createMessagePackerInstructionPlan,
createSingleInstructionAtATimeMessagePackerInstructionPlan,
FOREVER_PROMISE,
instructionFactory,
transactionPercentFactory,
Expand Down Expand Up @@ -1437,6 +1438,42 @@ describe('createTransactionPlanner', () => {
singleTransactionPlan([instructionA, messagePackerB.get(0, txPercent(50)), instructionC]),
);
});

/**
* [Par] ─────────────────────────▶ [Tx: A + B + C]
* │
* ├── [A: 25%]
* └── [NonDivSeq]
* └── [MessagePackerWithMultipleIterations(B: 25%, C: 25%)]
*
* This tests a case where the entire message packer must be packed into a single transaction.
* It uses a message packer that has instructions that can fit, but require multiple iterations
* to be fully added.
*/
it('accumulates all instructions when a message packer requires multiple iterations in fitEntirePlanInsideMessage', async () => {
expect.assertions(1);
const createTransactionMessage = createMockTransactionMessage;
const { instruction, singleTransactionPlan, txPercent } = getHelpers(createTransactionMessage);
const planner = createTransactionPlanner({ createTransactionMessage });

const instructionA = instruction('A', txPercent(25));
const instructionB = instruction('B', txPercent(25));
const instructionC = instruction('C', txPercent(25));

const multiIterPacker = createSingleInstructionAtATimeMessagePackerInstructionPlan([
instructionB,
instructionC,
]);

await expect(
planner(
parallelInstructionPlan([
singleInstructionPlan(instructionA),
nonDivisibleSequentialInstructionPlan([multiIterPacker]),
]),
),
).resolves.toEqual(singleTransactionPlan([instructionA, instructionB, instructionC]));
});
});

describe('complex scenarios', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/instruction-plans/src/transaction-planner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ function fitEntirePlanInsideMessage(
// eslint-disable-next-line no-case-declarations
const messagePacker = instructionPlan.getMessagePacker();
while (!messagePacker.done()) {
newMessage = messagePacker.packMessageToCapacity(message);
newMessage = messagePacker.packMessageToCapacity(newMessage);
}
return newMessage;
default:
Expand Down
Loading