From cdab6643dbdc5f4c3676e706edc460dc1a9dc627 Mon Sep 17 00:00:00 2001 From: moosecat Date: Wed, 18 Dec 2024 08:20:06 -0800 Subject: [PATCH] Nour/pyth local val test (#1391) * fix local val tests attempt * pyth lazer works * fix anchor test --- programs/drift/src/instructions/keeper.rs | 2 +- .../src/instructions/pyth_lazer_oracle.rs | 2 +- sdk/package.json | 1 + sdk/src/driftClient.ts | 5 +- sdk/src/idl/drift.json | 2 +- sdk/src/util/pythOracleUtils.ts | 113 +++++++++--------- sdk/yarn.lock | 2 +- .../run-anchor-local-validator-tests.sh | 2 +- tests/pythLazer.ts | 10 +- tests/pythLazerData.ts | 2 +- 10 files changed, 75 insertions(+), 66 deletions(-) diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index 811777cd5..e426ce65e 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -696,7 +696,7 @@ pub fn place_swift_taker_order<'c: 'info, 'info>( // Set max slot for the order early so we set correct swift order id let order_slot = taker_order_params_message.slot; - if order_slot < clock.slot - 500 { + if order_slot < clock.slot.saturating_sub(500) { msg!( "Swift order slot {} is too old: must be within 500 slots of current slot", order_slot diff --git a/programs/drift/src/instructions/pyth_lazer_oracle.rs b/programs/drift/src/instructions/pyth_lazer_oracle.rs index c2ea27d4d..6535f5632 100644 --- a/programs/drift/src/instructions/pyth_lazer_oracle.rs +++ b/programs/drift/src/instructions/pyth_lazer_oracle.rs @@ -28,7 +28,7 @@ pub fn handle_update_pyth_lazer_oracle<'c: 'info, 'info>( &pyth_message, ix_idx - 1, 0, - 0, + 12, // 8 bytes for anchor ix discriminator, 4 bytes for borsh vec len encoding ); if verified.is_err() { diff --git a/sdk/package.json b/sdk/package.json index a0c1940f7..9123e4a33 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -45,6 +45,7 @@ "@pythnetwork/client": "2.5.3", "@pythnetwork/price-service-sdk": "1.7.1", "@pythnetwork/pyth-solana-receiver": "0.7.0", + "@solana/buffer-layout": "4.0.1", "@solana/spl-token": "0.3.7", "@solana/web3.js": "1.92.3", "@switchboard-xyz/on-demand": "1.2.42", diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 952404037..9d1ca6782 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -8575,12 +8575,11 @@ export class DriftClient { overrideIxCount?: number ): TransactionInstruction[] { const pythMessageBytes = Buffer.from(pythMessageHex, 'hex'); - const updateData = new Uint8Array(pythMessageBytes); const verifyIx = createMinimalEd25519VerifyIx( overrideIxCount || precedingIxs.length + 1, - 0, - updateData + 12, + pythMessageBytes ); const remainingAccountsMeta = feedIds.map((feedId) => { diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 22561a49b..785fd1816 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -14315,7 +14315,7 @@ { "code": 6288, "name": "InvalidSwiftOrderParam", - "msg": "Swift only available for market/oracle perp orders" + "msg": "Invalid swift order param" }, { "code": 6289, diff --git a/sdk/src/util/pythOracleUtils.ts b/sdk/src/util/pythOracleUtils.ts index 705ba1cfe..a25212ce6 100644 --- a/sdk/src/util/pythOracleUtils.ts +++ b/sdk/src/util/pythOracleUtils.ts @@ -1,8 +1,5 @@ -import { - SYSVAR_INSTRUCTIONS_PUBKEY, - TransactionInstruction, - Ed25519Program, -} from '@solana/web3.js'; +import { TransactionInstruction, Ed25519Program } from '@solana/web3.js'; +import * as BufferLayout from '@solana/buffer-layout'; export function trimFeedId(feedId: string): string { if (feedId.startsWith('0x')) { @@ -21,10 +18,14 @@ const PUBKEY_LEN = 32; const MAGIC_LEN = 4; const MESSAGE_SIZE_LEN = 2; -export function getEd25519ArgsFromHex(hex: string): { +export function getEd25519ArgsFromHex( + hex: string, + customInstructionIndex?: number +): { publicKey: Uint8Array; signature: Uint8Array; message: Uint8Array; + instructionIndex?: number; } { const cleanedHex = hex.startsWith('0x') ? hex.slice(2) : hex; const buffer = new Uint8Array(Buffer.from(cleanedHex, 'hex')); @@ -58,9 +59,38 @@ export function getEd25519ArgsFromHex(hex: string): { publicKey, signature, message, + instructionIndex: customInstructionIndex, }; } +const readUint16LE = (data: Uint8Array, offset: number) => { + return data[offset] | (data[offset + 1] << 8); +}; + +const ED25519_INSTRUCTION_LAYOUT = BufferLayout.struct< + Readonly<{ + messageDataOffset: number; + messageDataSize: number; + messageInstructionIndex: number; + numSignatures: number; + padding: number; + publicKeyInstructionIndex: number; + publicKeyOffset: number; + signatureInstructionIndex: number; + signatureOffset: number; + }> +>([ + BufferLayout.u8('numSignatures'), + BufferLayout.u8('padding'), + BufferLayout.u16('signatureOffset'), + BufferLayout.u16('signatureInstructionIndex'), + BufferLayout.u16('publicKeyOffset'), + BufferLayout.u16('publicKeyInstructionIndex'), + BufferLayout.u16('messageDataOffset'), + BufferLayout.u16('messageDataSize'), + BufferLayout.u16('messageInstructionIndex'), +]); + /** * Constructs a minimal Ed25519 verification instruction that references the data * inside the main instruction (postPythLazerOracleUpdate). @@ -79,58 +109,31 @@ export function createMinimalEd25519VerifyIx( const messageDataSizeOffset = publicKeyOffset + PUBKEY_LEN; const messageDataOffset = messageDataSizeOffset + MESSAGE_SIZE_LEN; - if (messageDataOffset > customInstructionData.length) { - throw new Error('Not enough data in main instruction to read message size'); - } + const messageDataSize = readUint16LE( + customInstructionData, + messageDataSizeOffset - messageOffset + ); - const messageSize = - customInstructionData[messageDataSizeOffset] | - (customInstructionData[messageDataSizeOffset + 1] << 8); - - // Construct Ed25519SignatureOffsets - // struct Ed25519SignatureOffsets (14 bytes): - // u16 signature_offset - // u16 signature_instruction_index - // u16 public_key_offset - // u16 public_key_instruction_index - // u16 message_data_offset - // u16 message_data_size - // u16 message_instruction_index - const offsets = new Uint8Array(14); - const dv = new DataView(offsets.buffer); - - let byteOffset = 0; - dv.setUint16(byteOffset, signatureOffset, true); - byteOffset += 2; - dv.setUint16(byteOffset, customInstructionIndex, true); - byteOffset += 2; - dv.setUint16(byteOffset, publicKeyOffset, true); - byteOffset += 2; - dv.setUint16(byteOffset, customInstructionIndex, true); - byteOffset += 2; - dv.setUint16(byteOffset, messageDataOffset, true); - byteOffset += 2; - dv.setUint16(byteOffset, messageSize, true); - byteOffset += 2; - dv.setUint16(byteOffset, customInstructionIndex, true); - byteOffset += 2; - - const numSignatures = 1; - const padding = 0; - const ixData = new Uint8Array(2 + offsets.length); - ixData[0] = numSignatures; - ixData[1] = padding; - ixData.set(offsets, 2); + const instructionData = Buffer.alloc(16); + + ED25519_INSTRUCTION_LAYOUT.encode( + { + numSignatures: 1, + padding: 0, + signatureOffset, + signatureInstructionIndex: customInstructionIndex, + publicKeyOffset, + publicKeyInstructionIndex: customInstructionIndex, + messageDataOffset, + messageDataSize: messageDataSize, + messageInstructionIndex: customInstructionIndex, + }, + instructionData + ); return new TransactionInstruction({ - keys: [ - { - pubkey: SYSVAR_INSTRUCTIONS_PUBKEY, - isSigner: false, - isWritable: false, - }, - ], + keys: [], programId: Ed25519Program.programId, - data: Buffer.from(ixData), + data: instructionData, }); } diff --git a/sdk/yarn.lock b/sdk/yarn.lock index 85fb9e30f..80317ef1b 100644 --- a/sdk/yarn.lock +++ b/sdk/yarn.lock @@ -493,7 +493,7 @@ bigint-buffer "^1.1.5" bignumber.js "^9.0.1" -"@solana/buffer-layout@^4.0.0", "@solana/buffer-layout@^4.0.1": +"@solana/buffer-layout@4.0.1", "@solana/buffer-layout@^4.0.0", "@solana/buffer-layout@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15" integrity sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA== diff --git a/test-scripts/run-anchor-local-validator-tests.sh b/test-scripts/run-anchor-local-validator-tests.sh index 5678eb24c..9b811ed80 100644 --- a/test-scripts/run-anchor-local-validator-tests.sh +++ b/test-scripts/run-anchor-local-validator-tests.sh @@ -6,8 +6,8 @@ fi export ANCHOR_WALLET=~/.config/solana/id.json test_files=( + pythLazer.ts placeAndMakeSwiftPerp.ts - # pythLazer.ts ) diff --git a/tests/pythLazer.ts b/tests/pythLazer.ts index 4fa255920..4decbe0d6 100644 --- a/tests/pythLazer.ts +++ b/tests/pythLazer.ts @@ -9,6 +9,7 @@ import { } from '../sdk/src'; import { PublicKey, + Transaction, TransactionMessage, VersionedTransaction, } from '@solana/web3.js'; @@ -95,9 +96,10 @@ describe('pyth lazer oracles', () => { }); it('crank', async () => { - const ixs = await driftClient.getPostPythLazerOracleUpdateIxs( + const ixs = driftClient.getPostPythLazerOracleUpdateIxs( [1], - PYTH_LAZER_HEX_STRING_BTC + PYTH_LAZER_HEX_STRING_BTC, + [] ); const message = new TransactionMessage({ @@ -109,6 +111,10 @@ describe('pyth lazer oracles', () => { const simResult = await provider.connection.simulateTransaction(tx); console.log(simResult.value.logs); assert(simResult.value.err === null); + + const normalTx = new Transaction(); + normalTx.add(...ixs); + await driftClient.sendTransaction(normalTx); }); it('crank multi', async () => { diff --git a/tests/pythLazerData.ts b/tests/pythLazerData.ts index 0867b35cb..a6065b6fa 100644 --- a/tests/pythLazerData.ts +++ b/tests/pythLazerData.ts @@ -1,7 +1,7 @@ export const PYTH_STORAGE_DATA = '0XX/ucSvRAkL/td28gTUmmjn6CkzKyvYXJOMcup4pEKu3cXcP7cvDAH2UhC+5Pz1sc7h5Tf6vP2VAQKXZTuUrwTUVPxHPpSDT+g2BnoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=='; export const PYTH_LAZER_HEX_STRING_BTC = - 'b9011a82cb2701fd7ef39a113d32e1f8e5b759e1a0d41aa2f587521979d3ff10178b34b56bb9e2c0bdbc5a55fe2162ed81e90b5d0d36bd43286f9e99e49e242a00dd860ef65210bee4fcf5b1cee1e537fabcfd95010297653b94af04d454fc473e94834f1c0075d3c79340d732d48f2806000301010000000100daa8b56fe2080000'; + 'b9011a82c6e392a8837694bb546244b833da502497fe0b3a78b09295d5e9832d1d7b11fbb06ae6a93a78bb5988079259006fe79b32ab5983013464f358cd56ceb5a26a0df65210bee4fcf5b1cee1e537fabcfd95010297653b94af04d454fc473e94834f1c0075d3c79340b0d7837f2906000301010000000100677a0e79a6090000'; export const PYTH_LAZER_HEX_STRING_SOL = 'b9011a8286e4219d980a176145b777aff894df914947c33855028f2b993a8963b131270c9cbcccd282685eb24bdeffcb8bec8c5203f6c882ad2e8b9adb0d1ba6f89b3e09f65210bee4fcf5b1cee1e537fabcfd95010297653b94af04d454fc473e94834f1c0075d3c79340e90513da2806000301060000000100fd09283605000000';