diff --git a/contracts/MIPSMemory.sol b/contracts/MIPSMemory.sol
index f922c50f6a7dae03b2d32bed1bb42465eed9bf17..790aae10465dc0e8e10a4bf6bdb8c9437ff64945 100644
--- a/contracts/MIPSMemory.sol
+++ b/contracts/MIPSMemory.sol
@@ -36,13 +36,19 @@ contract MIPSMemory {
   }
 
   // one per owner (at a time)
-  mapping(address => uint64[25]) public largePreimage;
-  // TODO: also track the offset into the largePreimage to know what to store
 
-  function AddLargePreimageInit() public {
+  struct LargePreimage {
+    uint offset;
+  }
+  mapping(address => LargePreimage) public largePreimage;
+  // sadly due to soldiity limitations this can't be in the LargePreimage struct
+  mapping(address => uint64[25]) public largePreimageState;
+
+  function AddLargePreimageInit(uint offset) public {
     Lib_Keccak256.CTX memory c;
     Lib_Keccak256.keccak_init(c);
-    largePreimage[msg.sender] = c.A;
+    largePreimageState[msg.sender] = c.A;
+    largePreimage[msg.sender].offset = offset;
   }
 
   // TODO: input 136 bytes, as many times as you'd like
@@ -50,18 +56,26 @@ contract MIPSMemory {
   function AddLargePreimageUpdate(uint64[17] calldata data) public {
     // sha3_process_block
     Lib_Keccak256.CTX memory c;
-    c.A = largePreimage[msg.sender];
+    c.A = largePreimageState[msg.sender];
     for (uint i = 0; i < 17; i++) {
       c.A[i] ^= data[i];
     }
     Lib_Keccak256.sha3_permutation(c);
-    largePreimage[msg.sender] = c.A;
+    largePreimageState[msg.sender] = c.A;
   }
 
   // TODO: input <136 bytes and do the end of hash | 0x01 / | 0x80
-  function AddLargePreimageFinal() public view returns (bytes32) {
+  function AddLargePreimageFinal(uint64[17] calldata data) public view returns (bytes32) {
     Lib_Keccak256.CTX memory c;
-    c.A = largePreimage[msg.sender];
+    c.A = largePreimageState[msg.sender];
+
+    // TODO: check data is valid as the final block
+    // maybe even modify it
+    for (uint i = 0; i < 17; i++) {
+      c.A[i] ^= data[i];
+    }
+    Lib_Keccak256.sha3_permutation(c);
+
     // TODO: do this properly and save the hash
     // when this is updated, it won't be "view"
     return Lib_Keccak256.get_hash(c);
diff --git a/test/libkeccak.js b/test/libkeccak.js
index cfe0a119cd4866555152a1becd7ad9bcfbaba1c1..90ca9856956b47e11ba3ca61ff74e8c75a829c18 100644
--- a/test/libkeccak.js
+++ b/test/libkeccak.js
@@ -1,6 +1,9 @@
 const { keccak256 } = require("@ethersproject/keccak256");
 const { expect } = require("chai");
 
+const empty = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
+const endEmpty = [0x1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"0x8000000000000000"];
+
 describe("MIPSMemory contract", function () {
   it("Keccak should work", async function () {
     const [owner] = await ethers.getSigners();
@@ -9,20 +12,22 @@ describe("MIPSMemory contract", function () {
     const mm = await MIPSMemory.deploy();
     console.log("deployed at", mm.address, "by", owner.address);
 
-    await mm.AddLargePreimageInit();
+    await mm.AddLargePreimageInit(0);
     console.log("preimage initted");
 
+    // empty
+    expect(await mm.AddLargePreimageFinal(endEmpty)).to.equal(keccak256(new Uint8Array(0)));
+
     // block size is 136
-    //const a = ["0x0100000000000000",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x80];
-    const a = [0x1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"0x8000000000000000"];
-    await mm.AddLargePreimageUpdate(a);
+    await mm.AddLargePreimageUpdate(empty);
+
+    const hash = await mm.AddLargePreimageFinal(endEmpty);
     console.log("preimage updated");
 
     /*var tst1 = await mm.largePreimage(owner.address, 0);
     console.log(tst);*/
 
-    const hash = await mm.AddLargePreimageFinal();
-    const realhash = keccak256(new Uint8Array(0));
+    const realhash = keccak256(new Uint8Array(136));
     console.log("comp hash is", hash);
     console.log("real hash is", realhash);
     expect(hash).to.equal(realhash);