Files
Matt Hess 733ecd2681 Migrate all JS tests to Rust: 9-crate workspace, 703 tests, 0 JS remaining
Add root Cargo workspace with 9 crates: salvium-crypto (extended),
  salvium-types, salvium-consensus, salvium-wallet, salvium-tx,
  salvium-rpc, salvium-miner (extended), salvium-cli, salvium-multisig.

  New modules: chain_state, block_weight, alt_chain, validation,
  offline signing, stake lifecycle, wallet sync/query/encryption/utxo,
  randomx utilities, and full multisig crate with CARROT support.

  Delete 188 JS test/helper/debug files; archive integration test
  scripts to test/legacy-js/ for live testnet use. Testnet integration
  tests (transfer, stake, burn, convert, sweep) remain as #[ignore]-
  gated Rust tests runnable with --ignored against a live daemon.
2026-02-17 23:09:35 +00:00

113 lines
3.2 KiB
JavaScript

#!/usr/bin/env bun
// Debug transaction byte structure
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');
// Find SAL marker
let salOffset = -1;
for (let i = 0; i < Math.min(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(`Found SAL marker at offset ${i}`);
break;
}
}
if (salOffset === -1) {
console.log('No SAL marker found in first 200 bytes');
return;
}
let offset = salOffset;
// Read asset types (2 outputs)
console.log('\nAsset types:');
for (let i = 0; i < 2; i++) {
const len = data[offset++];
const str = new TextDecoder().decode(data.slice(offset, offset + len));
offset += len;
console.log(` Output ${i}: "${str}" (len=${len})`);
}
// Separator
if (data[offset] === 0x00) {
console.log(`Separator byte at ${offset}: 0x00`);
offset++;
}
// RCT type
const type = data[offset++];
console.log(`RCT type at ${offset-1}: ${type}`);
// Fee
const fee = decodeVarint(data, offset);
console.log(`Fee: ${fee.value} (${fee.bytesRead} bytes)`);
offset += fee.bytesRead;
// ecdhInfo (2 outputs, 8 bytes each for type 6)
console.log('\necdhInfo:');
for (let i = 0; i < 2; i++) {
const amount = data.slice(offset, offset + 8);
console.log(` Output ${i}: ${bytesToHex(amount)}`);
offset += 8;
}
// outPk (2 outputs, 32 bytes each)
console.log('\noutPk:');
for (let i = 0; i < 2; i++) {
const pk = data.slice(offset, offset + 32);
console.log(` Output ${i}: ${bytesToHex(pk).slice(0, 16)}...`);
offset += 32;
}
// p_r (32 bytes)
const p_r = data.slice(offset, offset + 32);
console.log(`\np_r: ${bytesToHex(p_r).slice(0, 16)}...`);
offset += 32;
console.log(`\nOffset after base RCT: ${offset}`);
console.log(`Remaining bytes: ${data.length - offset}`);
// What's next?
console.log(`\nNext 20 bytes: ${bytesToHex(data.slice(offset, offset + 20))}`);
// Try to read nbp (number of bulletproofs)
try {
const nbp = decodeVarint(data, offset);
console.log(`\nTrying to read nbp varint: ${nbp.value} (${nbp.bytesRead} bytes)`);
} catch (e) {
console.log(`\nFailed to read nbp: ${e.message}`);
}
}
test().catch(console.error);