Files
salvium-rs/test/debug-tx-bytes2.js
T
Matt Hess 56ab4ec3fe Convert debug-full-carrot-scan.js and verify-real-carrot.js to use
environment variables instead of hardcoded SPEND_SECRET values.

  Required env vars: SPEND_SECRET, TX_HASH
  Optional: DAEMON_URL (defaults to seed01.salvium.io:19081)
2026-01-22 03:24:00 +00:00

120 lines
3.7 KiB
JavaScript

#!/usr/bin/env bun
// Debug transaction byte structure - find correct RCT offset
import { createDaemonRPC } from '../src/rpc/index.js';
import { hexToBytes, bytesToHex } from '../src/address.js';
const daemon = createDaemonRPC({ url: 'http://seed01.salvium.io:19081', timeout: 30000 });
function decodeVarint(data, offset) {
let value = 0n;
let shift = 0n;
let bytesRead = 0;
while (offset + bytesRead < data.length) {
const byte = data[offset + bytesRead];
bytesRead++;
value |= BigInt(byte & 0x7f) << shift;
if ((byte & 0x80) === 0) break;
shift += 7n;
if (shift >= 70n) throw new Error('Varint overflow');
}
return { value: Number(value), bytesRead };
}
async function test() {
const txHash = 'a5e85d03e9200229a72fc3cfe94e5b139c8c2ad982dc3fd174898be63269e670';
const resp = await daemon.getTransactions([txHash], { decode_as_json: true });
const txData = resp.result?.txs?.[0];
const data = hexToBytes(txData.as_hex);
console.log('TX size:', data.length, 'bytes');
console.log('Daemon says: type=6, inputs=9, outputs=2\n');
let offset = 0;
// Version
const version = decodeVarint(data, offset);
console.log(`Version: ${version.value} at offset 0`);
offset += version.bytesRead;
// Unlock time
const unlockTime = decodeVarint(data, offset);
console.log(`Unlock time: ${unlockTime.value}`);
offset += unlockTime.bytesRead;
// Number of inputs
const numInputs = decodeVarint(data, offset);
console.log(`\nInputs: ${numInputs.value}`);
offset += numInputs.bytesRead;
// Skip inputs (simplified - just find pattern)
for (let i = 0; i < numInputs.value; i++) {
const inputType = data[offset++];
if (inputType === 0x02) { // txin_to_key
const amount = decodeVarint(data, offset);
offset += amount.bytesRead;
const numOffsets = decodeVarint(data, offset);
offset += numOffsets.bytesRead;
for (let j = 0; j < numOffsets.value; j++) {
const ko = decodeVarint(data, offset);
offset += ko.bytesRead;
}
offset += 32; // key image
}
}
console.log(`After inputs: offset ${offset}`);
// Number of outputs
const numOutputs = decodeVarint(data, offset);
console.log(`\nOutputs: ${numOutputs.value}`);
offset += numOutputs.bytesRead;
// Skip outputs
for (let i = 0; i < numOutputs.value; i++) {
const amount = decodeVarint(data, offset);
offset += amount.bytesRead;
const outputType = data[offset++];
if (outputType === 0x02) { // txout_to_key
offset += 32; // public key
} else if (outputType === 0x03) { // txout_to_tagged_key
offset += 32; // public key
offset += 1; // view tag
}
}
console.log(`After outputs: offset ${offset}`);
// Extra
const extraLen = decodeVarint(data, offset);
console.log(`\nExtra length: ${extraLen.value}`);
offset += extraLen.bytesRead;
offset += extraLen.value; // Skip extra bytes
console.log(`\n=== RCT section starts at offset ${offset} ===`);
console.log(`Remaining bytes: ${data.length - offset}`);
// Show first 50 bytes of RCT
console.log(`\nFirst 50 RCT bytes: ${bytesToHex(data.slice(offset, offset + 50))}`);
// Search for SAL marker from RCT start
let salOffset = -1;
for (let i = offset; i < Math.min(offset + 200, data.length - 4); i++) {
if (data[i] === 0x03 && data[i+1] === 0x53 && data[i+2] === 0x41 && data[i+3] === 0x4c) {
salOffset = i;
console.log(`\nFound SAL marker at offset ${i} (${i - offset} bytes into RCT)`);
break;
}
}
if (salOffset === -1) {
console.log('\nNo SAL marker found - checking raw RCT type byte');
const rawType = data[offset];
console.log(`Raw type byte at RCT start: 0x${rawType.toString(16)} (${rawType})`);
}
}
test().catch(console.error);