From 9cbfa916db30d2d9750b00fd89899ac1426f5cd1 Mon Sep 17 00:00:00 2001 From: steveluscher Date: Fri, 17 May 2024 20:58:29 +0000 Subject: [PATCH] The inspector will now accept a transaction, a versioned transaction, a message, or a versioned message --- app/components/inspector/RawInputCard.tsx | 98 +++++++++++++---------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/app/components/inspector/RawInputCard.tsx b/app/components/inspector/RawInputCard.tsx index ce0a5aed..90bba213 100755 --- a/app/components/inspector/RawInputCard.tsx +++ b/app/components/inspector/RawInputCard.tsx @@ -5,27 +5,62 @@ import React from 'react'; import type { TransactionData } from './InspectorPage'; -function deserializeTransaction(bytes: Uint8Array): { +function getMessageDataFromBytes(bytes: Uint8Array): { message: VersionedMessage; - signatures: string[]; -} | null { - const SIGNATURE_LENGTH = 64; + rawMessage: Uint8Array; +} { + const message = VersionedMessage.deserialize(bytes); + return { + message, + rawMessage: bytes, + }; +} + +function getTransactionDataFromUserSuppliedBytes(bytes: Uint8Array): { + message: VersionedMessage; + rawMessage: Uint8Array; + signatures?: string[]; +} { + /** + * Step 1: Try to parse the bytes as a *transaction* first (ie. with signatures at the front) + */ + let offset = 0; + const numSignatures = bytes[offset++]; + // If this were a transaction, would its message expect exactly `numSignatures`? + let requiredSignaturesByteOffset = 1 + numSignatures * 64; + if (VersionedMessage.deserializeMessageVersion(bytes.slice(requiredSignaturesByteOffset)) !== 'legacy') { + requiredSignaturesByteOffset++; + } + const numRequiredSignaturesAccordingToMessage = bytes[requiredSignaturesByteOffset]; + if (numRequiredSignaturesAccordingToMessage !== numSignatures) { + // We looked ahead into the message and could not match the number of signatures indicated + // by the first byte of the transaction with the expected number of signatures in the + // message. This is likely not a transaction at all, so try to parse it as a message now. + return getMessageDataFromBytes(bytes); + } const signatures = []; - try { - const signaturesLen = bytes[0]; - bytes = bytes.slice(1); - for (let i = 0; i < signaturesLen; i++) { - const rawSignature = bytes.slice(0, SIGNATURE_LENGTH); - bytes = bytes.slice(SIGNATURE_LENGTH); - signatures.push(base58.encode(rawSignature)); + for (let ii = 0; ii < numSignatures; ii++) { + const signatureBytes = bytes.subarray(offset, offset + 64); + if (signatureBytes.length !== 64) { + // We hit the end of the byte array before consuming `numSignatures` signatures. This + // can't have been a transaction, so try to parse it as a message now. + return getMessageDataFromBytes(bytes); } - } catch (err) { - // Errors above indicate that the bytes do not encode a transaction. - return null; + signatures.push(base58.encode(signatureBytes)); + offset += 64; + } + try { + const transactionData = getMessageDataFromBytes(bytes.slice(offset)); + return { + ...transactionData, + ...(signatures.length ? { signatures } : null), + }; + } catch { + /** + * Step 2: That didn't work, so presume that the bytes are a message, as asked for in the UI + */ + return getMessageDataFromBytes(bytes); } - - const message = VersionedMessage.deserialize(bytes); - return { message, signatures }; } export const MIN_MESSAGE_LENGTH = @@ -35,13 +70,6 @@ export const MIN_MESSAGE_LENGTH = 32 + // recent blockhash 1; // instructions length -const MIN_TRANSACTION_LENGTH = - 1 + // signatures length - 64 + // signatures, must have at least one for fees - MIN_MESSAGE_LENGTH; - -const MAX_TRANSACTION_SIGNATURES = Math.floor((1232 - MIN_TRANSACTION_LENGTH) / (64 + 32)) + 1; - export function RawInput({ value, setTransactionData, @@ -86,27 +114,9 @@ export function RawInput({ try { if (buffer.length < MIN_MESSAGE_LENGTH) { throw new Error('Input is not long enough to be valid.'); - } else if (buffer[0] > MAX_TRANSACTION_SIGNATURES) { - throw new Error(`Input starts with invalid byte: "${buffer[0]}"`); - } - - const tx = deserializeTransaction(buffer); - if (tx) { - const message = tx.message; - const rawMessage = message.serialize(); - setTransactionData({ - message, - rawMessage, - signatures: tx.signatures, - }); - } else { - const message = VersionedMessage.deserialize(buffer); - setTransactionData({ - message, - rawMessage: buffer, - }); } - + const transactionData = getTransactionDataFromUserSuppliedBytes(buffer); + setTransactionData(transactionData); setError(undefined); return; } catch (err) {