diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
index b557bbaa907bb11a900340b45ce79cf75e042010..1b8a900f65dc7b3ad0044a759efec25bb5047871 100644
--- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
+++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
@@ -26,10 +26,6 @@ import java.util.Map;
 /**
  * Encapsulates the database implementation and exposes high-level operations
  * to other components.
- * <p/>
- * This interface's methods are blocking, but they do not call out into other
- * components except to broadcast {@link org.briarproject.api.event.Event
- * Events}, so they can safely be called while holding locks.
  */
 public interface DatabaseComponent {
 
@@ -45,6 +41,9 @@ public interface DatabaseComponent {
 
 	/**
 	 * Starts a new transaction and returns an object representing it.
+	 * <p/>
+	 * This method acquires locks, so it must not be called while holding a
+	 * lock.
 	 * @param readOnly true if the transaction will only be used for reading.
 	 */
 	Transaction startTransaction(boolean readOnly) throws DbException;
diff --git a/briar-api/src/org/briarproject/api/transport/KeyManager.java b/briar-api/src/org/briarproject/api/transport/KeyManager.java
index 7cfd4d1eb98f1d978b68b330c8d66eac2f7d125d..8ee0578a860ab84eedde2a456d38822c3ee9338a 100644
--- a/briar-api/src/org/briarproject/api/transport/KeyManager.java
+++ b/briar-api/src/org/briarproject/api/transport/KeyManager.java
@@ -26,12 +26,14 @@ public interface KeyManager {
 	 * contact over the given transport, or null if an error occurs or the
 	 * contact does not support the transport.
 	 */
-	StreamContext getStreamContext(ContactId c, TransportId t);
+	StreamContext getStreamContext(ContactId c, TransportId t)
+			throws DbException;
 
 	/**
 	 * Looks up the given tag and returns a {@link StreamContext} for reading
 	 * from the corresponding stream, or null if an error occurs or the tag was
 	 * unexpected.
 	 */
-	StreamContext getStreamContext(TransportId t, byte[] tag);
+	StreamContext getStreamContext(TransportId t, byte[] tag)
+			throws DbException;
 }
diff --git a/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java b/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java
index 1ccc15e014cbbc87001f4ee4114c25070c7f87eb..2eb502cf1e929aa30f9dbed84d11a1ee6d069328 100644
--- a/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java
+++ b/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java
@@ -2,6 +2,7 @@ package org.briarproject.plugins;
 
 import org.briarproject.api.TransportId;
 import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.db.DbException;
 import org.briarproject.api.lifecycle.IoExecutor;
 import org.briarproject.api.plugins.ConnectionManager;
 import org.briarproject.api.plugins.ConnectionRegistry;
@@ -132,10 +133,14 @@ class ConnectionManagerImpl implements ConnectionManager {
 				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
 				disposeReader(true, false);
 				return;
+			} catch (DbException e) {
+				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				disposeReader(true, false);
+				return;
 			}
 			if (ctx == null) {
 				LOG.info("Unrecognised tag");
-				disposeReader(true, false);
+				disposeReader(false, false);
 				return;
 			}
 			ContactId contactId = ctx.getContactId();
@@ -176,11 +181,17 @@ class ConnectionManagerImpl implements ConnectionManager {
 
 		public void run() {
 			// Allocate a stream context
-			StreamContext ctx = keyManager.getStreamContext(contactId,
-					transportId);
+			StreamContext ctx;
+			try {
+				ctx = keyManager.getStreamContext(contactId, transportId);
+			} catch (DbException e) {
+				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				disposeWriter(true);
+				return;
+			}
 			if (ctx == null) {
 				LOG.warning("Could not allocate stream context");
-				disposeWriter(true);
+				disposeWriter(false);
 				return;
 			}
 			connectionRegistry.registerConnection(contactId, transportId);
@@ -232,10 +243,14 @@ class ConnectionManagerImpl implements ConnectionManager {
 				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
 				disposeReader(true, false);
 				return;
+			} catch (DbException e) {
+				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				disposeReader(true, false);
+				return;
 			}
 			if (ctx == null) {
 				LOG.info("Unrecognised tag");
-				disposeReader(true, false);
+				disposeReader(false, false);
 				return;
 			}
 			contactId = ctx.getContactId();
@@ -261,11 +276,17 @@ class ConnectionManagerImpl implements ConnectionManager {
 
 		private void runOutgoingSession() {
 			// Allocate a stream context
-			StreamContext ctx = keyManager.getStreamContext(contactId,
-					transportId);
+			StreamContext ctx;
+			try {
+				ctx = keyManager.getStreamContext(contactId, transportId);
+			} catch (DbException e) {
+				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				disposeWriter(true);
+				return;
+			}
 			if (ctx == null) {
 				LOG.warning("Could not allocate stream context");
-				disposeWriter(true);
+				disposeWriter(false);
 				return;
 			}
 			try {
@@ -320,11 +341,17 @@ class ConnectionManagerImpl implements ConnectionManager {
 
 		public void run() {
 			// Allocate a stream context
-			StreamContext ctx = keyManager.getStreamContext(contactId,
-					transportId);
+			StreamContext ctx;
+			try {
+				ctx = keyManager.getStreamContext(contactId, transportId);
+			} catch (DbException e) {
+				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				disposeWriter(true);
+				return;
+			}
 			if (ctx == null) {
 				LOG.warning("Could not allocate stream context");
-				disposeWriter(true);
+				disposeWriter(false);
 				return;
 			}
 			connectionRegistry.registerConnection(contactId, transportId);
@@ -357,6 +384,10 @@ class ConnectionManagerImpl implements ConnectionManager {
 				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
 				disposeReader(true, true);
 				return;
+			} catch (DbException e) {
+				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				disposeReader(true, true);
+				return;
 			}
 			// Unrecognised tags are suspicious in this case
 			if (ctx == null) {
diff --git a/briar-core/src/org/briarproject/transport/KeyManagerImpl.java b/briar-core/src/org/briarproject/transport/KeyManagerImpl.java
index 6b3093a45f8b721f8de4c69613c1717b3585245a..32a6ee3aada1dd7cb4f8b2a03b1382dc7b622765 100644
--- a/briar-core/src/org/briarproject/transport/KeyManagerImpl.java
+++ b/briar-core/src/org/briarproject/transport/KeyManagerImpl.java
@@ -22,7 +22,6 @@ import org.briarproject.api.system.Timer;
 import org.briarproject.api.transport.KeyManager;
 import org.briarproject.api.transport.StreamContext;
 
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -32,6 +31,7 @@ import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
+import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
 
 class KeyManagerImpl implements KeyManager, Service, EventListener {
@@ -65,31 +65,29 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
 
 	@Override
 	public boolean start() {
-		Map<TransportId, Integer> latencies =
+		Map<TransportId, Integer> transports =
 				new HashMap<TransportId, Integer>();
 		for (SimplexPluginFactory f : pluginConfig.getSimplexFactories())
-			latencies.put(f.getId(), f.getMaxLatency());
+			transports.put(f.getId(), f.getMaxLatency());
 		for (DuplexPluginFactory f : pluginConfig.getDuplexFactories())
-			latencies.put(f.getId(), f.getMaxLatency());
+			transports.put(f.getId(), f.getMaxLatency());
 		try {
-			Collection<Contact> contacts;
 			Transaction txn = db.startTransaction(false);
 			try {
-				contacts = db.getContacts(txn);
-				for (Entry<TransportId, Integer> e : latencies.entrySet())
+				for (Contact c : db.getContacts(txn))
+					if (c.isActive()) activeContacts.put(c.getId(), true);
+				for (Entry<TransportId, Integer> e : transports.entrySet())
 					db.addTransport(txn, e.getKey(), e.getValue());
+				for (Entry<TransportId, Integer> e : transports.entrySet()) {
+					TransportKeyManager m = new TransportKeyManager(db, crypto,
+							timer, clock, e.getKey(), e.getValue());
+					managers.put(e.getKey(), m);
+					m.start(txn);
+				}
 				txn.setComplete();
 			} finally {
 				db.endTransaction(txn);
 			}
-			for (Contact c : contacts)
-				if (c.isActive()) activeContacts.put(c.getId(), true);
-			for (Entry<TransportId, Integer> e : latencies.entrySet()) {
-				TransportKeyManager m = new TransportKeyManager(db, crypto,
-						timer, clock, e.getKey(), e.getValue());
-				managers.put(e.getKey(), m);
-				m.start();
-			}
 		} catch (DbException e) {
 			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
 			return false;
@@ -108,32 +106,43 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
 			m.addContact(txn, c, master, timestamp, alice);
 	}
 
-	public StreamContext getStreamContext(ContactId c, TransportId t) {
+	public StreamContext getStreamContext(ContactId c, TransportId t)
+			throws DbException {
 		// Don't allow outgoing streams to inactive contacts
 		if (!activeContacts.containsKey(c)) return null;
 		TransportKeyManager m = managers.get(t);
-		return m == null ? null : m.getStreamContext(c);
+		if (m == null) {
+			if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
+			return null;
+		}
+		StreamContext ctx = null;
+		Transaction txn = db.startTransaction(false);
+		try {
+			ctx = m.getStreamContext(txn, c);
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
+		}
+		return ctx;
 	}
 
-	public StreamContext getStreamContext(TransportId t, byte[] tag) {
+	public StreamContext getStreamContext(TransportId t, byte[] tag)
+			throws DbException {
 		TransportKeyManager m = managers.get(t);
-		if (m == null) return null;
-		StreamContext ctx = m.getStreamContext(tag);
-		if (ctx == null) return null;
-		// Activate the contact if not already active
-		if (!activeContacts.containsKey(ctx.getContactId())) {
-			try {
-				Transaction txn = db.startTransaction(false);
-				try {
-					db.setContactActive(txn, ctx.getContactId(), true);
-					txn.setComplete();
-				} finally {
-					db.endTransaction(txn);
-				}
-			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				return null;
-			}
+		if (m == null) {
+			if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
+			return null;
+		}
+		StreamContext ctx = null;
+		Transaction txn = db.startTransaction(false);
+		try {
+			ctx = m.getStreamContext(txn, tag);
+			// Activate the contact if not already active
+			if (ctx != null && !activeContacts.containsKey(ctx.getContactId()))
+				db.setContactActive(txn, ctx.getContactId(), true);
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
 		}
 		return ctx;
 	}
diff --git a/briar-core/src/org/briarproject/transport/TransportKeyManager.java b/briar-core/src/org/briarproject/transport/TransportKeyManager.java
index 8b1ee0a5857a2a57fd812219fb9fd3aaca021fbc..0c9b95bf4679e5d6bb09f95a8b733b83dc1b605c 100644
--- a/briar-core/src/org/briarproject/transport/TransportKeyManager.java
+++ b/briar-core/src/org/briarproject/transport/TransportKeyManager.java
@@ -60,46 +60,20 @@ class TransportKeyManager {
 		keys = new HashMap<ContactId, MutableTransportKeys>();
 	}
 
-	void start() {
+	void start(Transaction txn) throws DbException {
 		long now = clock.currentTimeMillis();
 		lock.lock();
 		try {
 			// Load the transport keys from the DB
-			Map<ContactId, TransportKeys> loaded;
-			try {
-				Transaction txn = db.startTransaction(true);
-				try {
-					loaded = db.getTransportKeys(txn, transportId);
-					txn.setComplete();
-				} finally {
-					db.endTransaction(txn);
-				}
-			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				return;
-			}
+			Map<ContactId, TransportKeys> loaded =
+					db.getTransportKeys(txn, transportId);
 			// Rotate the keys to the current rotation period
-			Map<ContactId, TransportKeys> rotated =
-					new HashMap<ContactId, TransportKeys>();
-			Map<ContactId, TransportKeys> current =
-					new HashMap<ContactId, TransportKeys>();
-			long rotationPeriod = now / rotationPeriodLength;
-			for (Entry<ContactId, TransportKeys> e : loaded.entrySet()) {
-				ContactId c = e.getKey();
-				TransportKeys k = e.getValue();
-				TransportKeys k1 = crypto.rotateTransportKeys(k,
-						rotationPeriod);
-				if (k1.getRotationPeriod() > k.getRotationPeriod())
-					rotated.put(c, k1);
-				current.put(c, k1);
-			}
+			RotationResult rotationResult = rotateKeys(loaded, now);
 			// Initialise mutable state for all contacts
-			for (Entry<ContactId, TransportKeys> e : current.entrySet())
-				addKeys(e.getKey(), new MutableTransportKeys(e.getValue()));
+			addKeys(rotationResult.current);
 			// Write any rotated keys back to the DB
-			updateTransportKeys(rotated);
-		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			if (!rotationResult.rotated.isEmpty())
+				db.updateTransportKeys(txn, rotationResult.rotated);
 		} finally {
 			lock.unlock();
 		}
@@ -107,6 +81,27 @@ class TransportKeyManager {
 		scheduleKeyRotation(now);
 	}
 
+	private RotationResult rotateKeys(Map<ContactId, TransportKeys> keys,
+			long now) {
+		RotationResult rotationResult = new RotationResult();
+		long rotationPeriod = now / rotationPeriodLength;
+		for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
+			ContactId c = e.getKey();
+			TransportKeys k = e.getValue();
+			TransportKeys k1 = crypto.rotateTransportKeys(k, rotationPeriod);
+			if (k1.getRotationPeriod() > k.getRotationPeriod())
+				rotationResult.rotated.put(c, k1);
+			rotationResult.current.put(c, k1);
+		}
+		return rotationResult;
+	}
+
+	// Locking: lock
+	private void addKeys(Map<ContactId, TransportKeys> m) {
+		for (Entry<ContactId, TransportKeys> e : m.entrySet())
+			addKeys(e.getKey(), new MutableTransportKeys(e.getValue()));
+	}
+
 	// Locking: lock
 	private void addKeys(ContactId c, MutableTransportKeys m) {
 		encodeTags(c, m.getPreviousIncomingKeys());
@@ -126,23 +121,21 @@ class TransportKeyManager {
 		}
 	}
 
-	private void updateTransportKeys(Map<ContactId, TransportKeys> rotated)
-		throws DbException {
-		if (!rotated.isEmpty()) {
-			Transaction txn = db.startTransaction(false);
-			try {
-				db.updateTransportKeys(txn, rotated);
-				txn.setComplete();
-			} finally {
-				db.endTransaction(txn);
-			}
-		}
-	}
-
 	private void scheduleKeyRotation(long now) {
 		TimerTask task = new TimerTask() {
 			public void run() {
-				rotateKeys();
+				try {
+					Transaction txn = db.startTransaction(false);
+					try {
+						rotateKeys(txn);
+						txn.setComplete();
+					} finally {
+						db.endTransaction(txn);
+					}
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+				}
 			}
 		};
 		long delay = rotationPeriodLength - now % rotationPeriodLength;
@@ -185,7 +178,8 @@ class TransportKeyManager {
 		}
 	}
 
-	StreamContext getStreamContext(ContactId c) {
+	StreamContext getStreamContext(Transaction txn, ContactId c)
+			throws DbException {
 		lock.lock();
 		try {
 			// Look up the outgoing keys for the contact
@@ -198,24 +192,16 @@ class TransportKeyManager {
 					outKeys.getStreamCounter());
 			// Increment the stream counter and write it back to the DB
 			outKeys.incrementStreamCounter();
-			Transaction txn = db.startTransaction(false);
-			try {
-				db.incrementStreamCounter(txn, c, transportId,
-						outKeys.getRotationPeriod());
-				txn.setComplete();
-			} finally {
-				db.endTransaction(txn);
-			}
+			db.incrementStreamCounter(txn, c, transportId,
+					outKeys.getRotationPeriod());
 			return ctx;
-		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			return null;
 		} finally {
 			lock.unlock();
 		}
 	}
 
-	StreamContext getStreamContext(byte[] tag) {
+	StreamContext getStreamContext(Transaction txn, byte[] tag)
+			throws DbException {
 		lock.lock();
 		try {
 			// Look up the incoming keys for the tag
@@ -244,53 +230,33 @@ class TransportKeyManager {
 				inContexts.remove(new Bytes(removeTag));
 			}
 			// Write the window back to the DB
-			Transaction txn = db.startTransaction(false);
-			try {
-				db.setReorderingWindow(txn, tagCtx.contactId, transportId,
-						inKeys.getRotationPeriod(), window.getBase(),
-						window.getBitmap());
-				txn.setComplete();
-			} finally {
-				db.endTransaction(txn);
-			}
+			db.setReorderingWindow(txn, tagCtx.contactId, transportId,
+					inKeys.getRotationPeriod(), window.getBase(),
+					window.getBitmap());
 			return ctx;
-		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			return null;
 		} finally {
 			lock.unlock();
 		}
 	}
 
-	private void rotateKeys() {
+	private void rotateKeys(Transaction txn) throws DbException {
 		long now = clock.currentTimeMillis();
 		lock.lock();
 		try {
 			// Rotate the keys to the current rotation period
-			Map<ContactId, TransportKeys> rotated =
-					new HashMap<ContactId, TransportKeys>();
-			Map<ContactId, TransportKeys> current =
+			Map<ContactId, TransportKeys> snapshot =
 					new HashMap<ContactId, TransportKeys>();
-			long rotationPeriod = now / rotationPeriodLength;
-			for (Entry<ContactId, MutableTransportKeys> e : keys.entrySet()) {
-				ContactId c = e.getKey();
-				TransportKeys k = e.getValue().snapshot();
-				TransportKeys k1 = crypto.rotateTransportKeys(k,
-						rotationPeriod);
-				if (k1.getRotationPeriod() > k.getRotationPeriod())
-					rotated.put(c, k1);
-				current.put(c, k1);
-			}
+			for (Entry<ContactId, MutableTransportKeys> e : keys.entrySet())
+				snapshot.put(e.getKey(), e.getValue().snapshot());
+			RotationResult rotationResult = rotateKeys(snapshot, now);
 			// Rebuild the mutable state for all contacts
 			inContexts.clear();
 			outContexts.clear();
 			keys.clear();
-			for (Entry<ContactId, TransportKeys> e : current.entrySet())
-				addKeys(e.getKey(), new MutableTransportKeys(e.getValue()));
+			addKeys(rotationResult.current);
 			// Write any rotated keys back to the DB
-			updateTransportKeys(rotated);
-		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			if (!rotationResult.rotated.isEmpty())
+				db.updateTransportKeys(txn, rotationResult.rotated);
 		} finally {
 			lock.unlock();
 		}
@@ -311,4 +277,14 @@ class TransportKeyManager {
 			this.streamNumber = streamNumber;
 		}
 	}
+
+	private static class RotationResult {
+
+		private final Map<ContactId, TransportKeys> current, rotated;
+
+		private RotationResult() {
+			current = new HashMap<ContactId, TransportKeys>();
+			rotated = new HashMap<ContactId, TransportKeys>();
+		}
+	}
 }
diff --git a/briar-tests/src/org/briarproject/transport/TransportKeyManagerTest.java b/briar-tests/src/org/briarproject/transport/TransportKeyManagerTest.java
index 5b674dbdb59f540d12b28cd5c82dbbc710b82691..7fdfbcf7b3a1aeb30fc0a77b076cc1575487c393 100644
--- a/briar-tests/src/org/briarproject/transport/TransportKeyManagerTest.java
+++ b/briar-tests/src/org/briarproject/transport/TransportKeyManagerTest.java
@@ -37,6 +37,7 @@ import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 public class TransportKeyManagerTest extends BriarTestCase {
 
@@ -57,7 +58,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		final CryptoComponent crypto = context.mock(CryptoComponent.class);
 		final Timer timer = context.mock(Timer.class);
 		final Clock clock = context.mock(Clock.class);
-		final Transaction txn = new Transaction(null, true);
+
 		final Map<ContactId, TransportKeys> loaded =
 				new LinkedHashMap<ContactId, TransportKeys>();
 		final TransportKeys shouldRotate = createTransportKeys(900, 0);
@@ -65,17 +66,15 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		loaded.put(contactId, shouldRotate);
 		loaded.put(contactId1, shouldNotRotate);
 		final TransportKeys rotated = createTransportKeys(1000, 0);
-		final Transaction txn1 = new Transaction(null, false);
+		final Transaction txn = new Transaction(null, false);
+
 		context.checking(new Expectations() {{
 			// Get the current time (1 ms after start of rotation period 1000)
 			oneOf(clock).currentTimeMillis();
 			will(returnValue(rotationPeriodLength * 1000 + 1));
 			// Load the transport keys
-			oneOf(db).startTransaction(true);
-			will(returnValue(txn));
 			oneOf(db).getTransportKeys(txn, transportId);
 			will(returnValue(loaded));
-			oneOf(db).endTransaction(txn);
 			// Rotate the transport keys
 			oneOf(crypto).rotateTransportKeys(shouldRotate, 1000);
 			will(returnValue(rotated));
@@ -88,11 +87,8 @@ public class TransportKeyManagerTest extends BriarTestCase {
 				will(new EncodeTagAction());
 			}
 			// Save the keys that were rotated
-			oneOf(db).startTransaction(false);
-			will(returnValue(txn1));
-			oneOf(db).updateTransportKeys(txn1,
+			oneOf(db).updateTransportKeys(txn,
 					Collections.singletonMap(contactId, rotated));
-			oneOf(db).endTransaction(txn1);
 			// Schedule key rotation at the start of the next rotation period
 			oneOf(timer).schedule(with(any(TimerTask.class)),
 					with(rotationPeriodLength - 1));
@@ -100,7 +96,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
 
 		TransportKeyManager transportKeyManager = new TransportKeyManager(db,
 				crypto, timer, clock, transportId, maxLatency);
-		transportKeyManager.start();
+		transportKeyManager.start(txn);
 
 		context.assertIsSatisfied();
 	}
@@ -112,10 +108,12 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		final CryptoComponent crypto = context.mock(CryptoComponent.class);
 		final Timer timer = context.mock(Timer.class);
 		final Clock clock = context.mock(Clock.class);
+
 		final boolean alice = true;
 		final TransportKeys transportKeys = createTransportKeys(999, 0);
 		final TransportKeys rotated = createTransportKeys(1000, 0);
 		final Transaction txn = new Transaction(null, false);
+
 		context.checking(new Expectations() {{
 			oneOf(crypto).deriveTransportKeys(transportId, masterKey, 999,
 					alice);
@@ -155,9 +153,11 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		final Timer timer = context.mock(Timer.class);
 		final Clock clock = context.mock(Clock.class);
 
+		final Transaction txn = new Transaction(null, false);
+
 		TransportKeyManager transportKeyManager = new TransportKeyManager(db,
 				crypto, timer, clock, transportId, maxLatency);
-		assertNull(transportKeyManager.getStreamContext(contactId));
+		assertNull(transportKeyManager.getStreamContext(txn, contactId));
 
 		context.assertIsSatisfied();
 	}
@@ -170,11 +170,13 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		final CryptoComponent crypto = context.mock(CryptoComponent.class);
 		final Timer timer = context.mock(Timer.class);
 		final Clock clock = context.mock(Clock.class);
+
 		final boolean alice = true;
 		// The stream counter has been exhausted
 		final TransportKeys transportKeys = createTransportKeys(1000,
 				MAX_32_BIT_UNSIGNED + 1);
 		final Transaction txn = new Transaction(null, false);
+
 		context.checking(new Expectations() {{
 			oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
 					alice);
@@ -201,7 +203,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		long timestamp = rotationPeriodLength * 1000;
 		transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
 				alice);
-		assertNull(transportKeyManager.getStreamContext(contactId));
+		assertNull(transportKeyManager.getStreamContext(txn, contactId));
 
 		context.assertIsSatisfied();
 	}
@@ -213,12 +215,13 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		final CryptoComponent crypto = context.mock(CryptoComponent.class);
 		final Timer timer = context.mock(Timer.class);
 		final Clock clock = context.mock(Clock.class);
+
 		final boolean alice = true;
 		// The stream counter can be used one more time before being exhausted
 		final TransportKeys transportKeys = createTransportKeys(1000,
 				MAX_32_BIT_UNSIGNED);
 		final Transaction txn = new Transaction(null, false);
-		final Transaction txn1 = new Transaction(null, false);
+
 		context.checking(new Expectations() {{
 			oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
 					alice);
@@ -238,11 +241,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
 			// Save the keys
 			oneOf(db).addTransportKeys(txn, contactId, transportKeys);
 			// Increment the stream counter
-			oneOf(db).startTransaction(false);
-			will(returnValue(txn1));
-			oneOf(db).incrementStreamCounter(txn1, contactId, transportId,
-					1000);
-			oneOf(db).endTransaction(txn1);
+			oneOf(db).incrementStreamCounter(txn, contactId, transportId, 1000);
 		}});
 
 		TransportKeyManager transportKeyManager = new TransportKeyManager(db,
@@ -252,7 +251,8 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
 				alice);
 		// The first request should return a stream context
-		StreamContext ctx = transportKeyManager.getStreamContext(contactId);
+		StreamContext ctx = transportKeyManager.getStreamContext(txn,
+				contactId);
 		assertNotNull(ctx);
 		assertEquals(contactId, ctx.getContactId());
 		assertEquals(transportId, ctx.getTransportId());
@@ -260,7 +260,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		assertEquals(headerKey, ctx.getHeaderKey());
 		assertEquals(MAX_32_BIT_UNSIGNED, ctx.getStreamNumber());
 		// The second request should return null, the counter is exhausted
-		assertNull(transportKeyManager.getStreamContext(contactId));
+		assertNull(transportKeyManager.getStreamContext(txn, contactId));
 
 		context.assertIsSatisfied();
 	}
@@ -273,9 +273,11 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		final CryptoComponent crypto = context.mock(CryptoComponent.class);
 		final Timer timer = context.mock(Timer.class);
 		final Clock clock = context.mock(Clock.class);
+
 		final boolean alice = true;
 		final TransportKeys transportKeys = createTransportKeys(1000, 0);
 		final Transaction txn = new Transaction(null, false);
+
 		context.checking(new Expectations() {{
 			oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
 					alice);
@@ -302,7 +304,8 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		long timestamp = rotationPeriodLength * 1000;
 		transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
 				alice);
-		assertNull(transportKeyManager.getStreamContext(new byte[TAG_LENGTH]));
+		assertNull(transportKeyManager.getStreamContext(txn,
+				new byte[TAG_LENGTH]));
 
 		context.assertIsSatisfied();
 	}
@@ -314,12 +317,13 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		final CryptoComponent crypto = context.mock(CryptoComponent.class);
 		final Timer timer = context.mock(Timer.class);
 		final Clock clock = context.mock(Clock.class);
+
 		final boolean alice = true;
 		final TransportKeys transportKeys = createTransportKeys(1000, 0);
-		final Transaction txn = new Transaction(null, false);
-		final Transaction txn1 = new Transaction(null, false);
 		// Keep a copy of the tags
 		final List<byte[]> tags = new ArrayList<byte[]>();
+		final Transaction txn = new Transaction(null, false);
+
 		context.checking(new Expectations() {{
 			oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
 					alice);
@@ -343,11 +347,8 @@ public class TransportKeyManagerTest extends BriarTestCase {
 					with(tagKey), with((long) REORDERING_WINDOW_SIZE));
 			will(new EncodeTagAction(tags));
 			// Save the reordering window (previous rotation period, base 1)
-			oneOf(db).startTransaction(false);
-			will(returnValue(txn1));
-			oneOf(db).setReorderingWindow(txn1, contactId, transportId, 999,
+			oneOf(db).setReorderingWindow(txn, contactId, transportId, 999,
 					1, new byte[REORDERING_WINDOW_SIZE / 8]);
-			oneOf(db).endTransaction(txn1);
 		}});
 
 		TransportKeyManager transportKeyManager = new TransportKeyManager(db,
@@ -360,7 +361,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size());
 		byte[] tag = tags.get(0);
 		// The first request should return a stream context
-		StreamContext ctx = transportKeyManager.getStreamContext(tag);
+		StreamContext ctx = transportKeyManager.getStreamContext(txn, tag);
 		assertNotNull(ctx);
 		assertEquals(contactId, ctx.getContactId());
 		assertEquals(transportId, ctx.getTransportId());
@@ -370,7 +371,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		// Another tag should have been encoded
 		assertEquals(REORDERING_WINDOW_SIZE * 3 + 1, tags.size());
 		// The second request should return null, the tag has already been used
-		assertNull(transportKeyManager.getStreamContext(tag));
+		assertNull(transportKeyManager.getStreamContext(txn, tag));
 
 		context.assertIsSatisfied();
 	}
@@ -382,22 +383,21 @@ public class TransportKeyManagerTest extends BriarTestCase {
 		final CryptoComponent crypto = context.mock(CryptoComponent.class);
 		final Timer timer = context.mock(Timer.class);
 		final Clock clock = context.mock(Clock.class);
-		final Transaction txn = new Transaction(null, true);
+
 		final TransportKeys transportKeys = createTransportKeys(1000, 0);
 		final Map<ContactId, TransportKeys> loaded =
 				Collections.singletonMap(contactId, transportKeys);
 		final TransportKeys rotated = createTransportKeys(1001, 0);
+		final Transaction txn = new Transaction(null, false);
 		final Transaction txn1 = new Transaction(null, false);
+
 		context.checking(new Expectations() {{
 			// Get the current time (the start of rotation period 1000)
 			oneOf(clock).currentTimeMillis();
 			will(returnValue(rotationPeriodLength * 1000));
 			// Load the transport keys
-			oneOf(db).startTransaction(true);
-			will(returnValue(txn));
 			oneOf(db).getTransportKeys(txn, transportId);
 			will(returnValue(loaded));
-			oneOf(db).endTransaction(txn);
 			// Rotate the transport keys (the keys are unaffected)
 			oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
 			will(returnValue(transportKeys));
@@ -411,6 +411,9 @@ public class TransportKeyManagerTest extends BriarTestCase {
 			oneOf(timer).schedule(with(any(TimerTask.class)),
 					with(rotationPeriodLength));
 			will(new RunTimerTaskAction());
+			// Start a transaction for key rotation
+			oneOf(db).startTransaction(false);
+			will(returnValue(txn1));
 			// Get the current time (the start of rotation period 1001)
 			oneOf(clock).currentTimeMillis();
 			will(returnValue(rotationPeriodLength * 1001));
@@ -425,19 +428,19 @@ public class TransportKeyManagerTest extends BriarTestCase {
 				will(new EncodeTagAction());
 			}
 			// Save the keys that were rotated
-			oneOf(db).startTransaction(false);
-			will(returnValue(txn1));
 			oneOf(db).updateTransportKeys(txn1,
 					Collections.singletonMap(contactId, rotated));
-			oneOf(db).endTransaction(txn1);
 			// Schedule key rotation at the start of the next rotation period
 			oneOf(timer).schedule(with(any(TimerTask.class)),
 					with(rotationPeriodLength));
+			// Commit the key rotation transaction
+			oneOf(db).endTransaction(txn1);
 		}});
 
 		TransportKeyManager transportKeyManager = new TransportKeyManager(db,
 				crypto, timer, clock, transportId, maxLatency);
-		transportKeyManager.start();
+		transportKeyManager.start(txn);
+		assertTrue(txn1.isComplete());
 
 		context.assertIsSatisfied();
 	}