diff --git a/crates/idxdb-store/src/js/transactions.js b/crates/idxdb-store/src/js/transactions.js index 4955b27b..696ca2b7 100644 --- a/crates/idxdb-store/src/js/transactions.js +++ b/crates/idxdb-store/src/js/transactions.js @@ -120,6 +120,15 @@ export async function upsertTransactionRecord(dbId, transactionId, details, bloc throw err; } } +export async function upsertTransactionRecordWithScript(dbId, transactionId, details, blockNum, statusVariant, status, scriptRoot, txScript) { + const db = getDatabase(dbId); + await db.dexie.transaction("rw", [db.transactions, db.transactionScripts], async (tx) => { + if (scriptRoot && txScript) { + await insertTransactionScript(dbId, scriptRoot, txScript, tx); + } + await upsertTransactionRecord(dbId, transactionId, details, blockNum, statusVariant, status, scriptRoot, tx); + }); +} /** * Applies a batch of transaction updates atomically inside a single Dexie transaction. * diff --git a/crates/idxdb-store/src/transaction/js_bindings.rs b/crates/idxdb-store/src/transaction/js_bindings.rs index 3b517bc6..2234cabf 100644 --- a/crates/idxdb-store/src/transaction/js_bindings.rs +++ b/crates/idxdb-store/src/transaction/js_bindings.rs @@ -12,15 +12,8 @@ extern "C" { #[wasm_bindgen(js_name = getTransactions)] pub fn idxdb_get_transactions(db_id: &str, filter: String) -> js_sys::Promise; - #[wasm_bindgen(js_name = insertTransactionScript)] - pub fn idxdb_insert_transaction_script( - db_id: &str, - script_root: Vec, - tx_script: Option>, - ) -> js_sys::Promise; - - #[wasm_bindgen(js_name = upsertTransactionRecord)] - pub fn idxdb_upsert_transaction_record( + #[wasm_bindgen(js_name = upsertTransactionRecordWithScript)] + pub fn idxdb_upsert_transaction_record_with_script( db_id: &str, transaction_id: String, details: Vec, @@ -28,6 +21,7 @@ extern "C" { statusVariant: u8, status: Vec, scriptRoot: Option>, + txScript: Option>, ) -> js_sys::Promise; #[wasm_bindgen(js_name = applyTransactionBatch)] diff --git a/crates/idxdb-store/src/transaction/utils.rs b/crates/idxdb-store/src/transaction/utils.rs index 4a9d5242..eab1e2cd 100644 --- a/crates/idxdb-store/src/transaction/utils.rs +++ b/crates/idxdb-store/src/transaction/utils.rs @@ -15,7 +15,7 @@ use miden_client::utils::Serializable; use serde::Serialize; use wasm_bindgen::prelude::wasm_bindgen; -use super::js_bindings::{idxdb_insert_transaction_script, idxdb_upsert_transaction_record}; +use super::js_bindings::idxdb_upsert_transaction_record_with_script; use crate::promise::await_ok; // TYPES @@ -111,12 +111,7 @@ pub(crate) async fn upsert_transaction_record( ) -> Result<(), StoreError> { let serialized_data = serialize_transaction_record(transaction); - if let Some(root) = serialized_data.script_root.clone() { - let promise = idxdb_insert_transaction_script(db_id, root, serialized_data.tx_script); - await_ok(promise, "failed to insert script").await?; - } - - let promise = idxdb_upsert_transaction_record( + let promise = idxdb_upsert_transaction_record_with_script( db_id, serialized_data.id, serialized_data.details, @@ -124,6 +119,7 @@ pub(crate) async fn upsert_transaction_record( serialized_data.status_variant, serialized_data.status, serialized_data.script_root, + serialized_data.tx_script, ); await_ok(promise, "failed to insert transaction data").await?; diff --git a/crates/idxdb-store/src/ts/transactions.test.ts b/crates/idxdb-store/src/ts/transactions.test.ts index ed130d1d..f2f71507 100644 --- a/crates/idxdb-store/src/ts/transactions.test.ts +++ b/crates/idxdb-store/src/ts/transactions.test.ts @@ -4,6 +4,7 @@ import { getTransactions, insertTransactionScript, upsertTransactionRecord, + upsertTransactionRecordWithScript, } from "./transactions.js"; let dbCounter = 0; @@ -390,6 +391,33 @@ describe("transactions", () => { expect(all[0].txScript).toEqual(script2); }); + it("rolls back the script when the transaction record write fails", async () => { + const dbId = await openTestDb(); + const scriptRootBytes = new Uint8Array([0x0a, 0x0b]); + const txScriptBytes = new Uint8Array([0x0c, 0x0d]); + + await expect( + upsertTransactionRecordWithScript( + dbId, + undefined as unknown as string, + new Uint8Array([1]), + 1, + 0, + new Uint8Array([2]), + scriptRootBytes, + txScriptBytes + ) + ).rejects.toThrow(); + + const db = getDatabase(dbId); + const storedScript = await db.transactionScripts + .where("scriptRoot") + .equals(toBase64(scriptRootBytes)) + .first(); + + expect(storedScript).toBeUndefined(); + }); + // ------------------------------------------------------------------------- // Error paths — "never-opened" dbId // ------------------------------------------------------------------------- diff --git a/crates/idxdb-store/src/ts/transactions.ts b/crates/idxdb-store/src/ts/transactions.ts index 98bdcf2e..8c3df2cb 100644 --- a/crates/idxdb-store/src/ts/transactions.ts +++ b/crates/idxdb-store/src/ts/transactions.ts @@ -175,6 +175,40 @@ export async function upsertTransactionRecord( } } +export async function upsertTransactionRecordWithScript( + dbId: string, + transactionId: string, + details: Uint8Array, + blockNum: number, + statusVariant: number, + status: Uint8Array, + scriptRoot?: Uint8Array, + txScript?: Uint8Array +) { + const db = getDatabase(dbId); + + await db.dexie.transaction( + "rw", + [db.transactions, db.transactionScripts], + async (tx) => { + if (scriptRoot && txScript) { + await insertTransactionScript(dbId, scriptRoot, txScript, tx); + } + + await upsertTransactionRecord( + dbId, + transactionId, + details, + blockNum, + statusVariant, + status, + scriptRoot, + tx + ); + } + ); +} + // BATCH APPLY // ================================================================================================