diff --git a/api/net/sf/briar/api/protocol/ProtocolConstants.java b/api/net/sf/briar/api/protocol/ProtocolConstants.java
index 7e23d247a0474010ab9a5aaba6c860f2a48168e1..ba3f5fb6581a4c8ce6cbea121b4e296903f11419 100644
--- a/api/net/sf/briar/api/protocol/ProtocolConstants.java
+++ b/api/net/sf/briar/api/protocol/ProtocolConstants.java
@@ -48,4 +48,7 @@ public interface ProtocolConstants {
 
 	/** The length of a message's random salt in bytes. */
 	static final int SALT_LENGTH = 8;
+
+	/** The size of the connection reordering window. */
+	static final int CONNECTION_WINDOW_SIZE = 32;
 }
diff --git a/api/net/sf/briar/api/transport/ConnectionWindow.java b/api/net/sf/briar/api/transport/ConnectionWindow.java
index a7e8eb98b2cb5bb29b6d08a980a6301b5aed8cf4..35fc872f15cee0f8cbe16902e841167a0fe0f63f 100644
--- a/api/net/sf/briar/api/transport/ConnectionWindow.java
+++ b/api/net/sf/briar/api/transport/ConnectionWindow.java
@@ -4,13 +4,9 @@ import java.util.Collection;
 
 public interface ConnectionWindow {
 
-	long getCentre();
-
-	int getBitmap();
-
 	boolean isSeen(long connection);
 
 	void setSeen(long connection);
 
-	Collection<Long> getUnseenConnectionNumbers();
+	Collection<Long> getUnseen();
 }
diff --git a/api/net/sf/briar/api/transport/ConnectionWindowFactory.java b/api/net/sf/briar/api/transport/ConnectionWindowFactory.java
index 40ad981528f39cf2815ac34f570ebc14f83c0ebe..33ef8e7a6aa9650d5356accd7242ffa85505d53e 100644
--- a/api/net/sf/briar/api/transport/ConnectionWindowFactory.java
+++ b/api/net/sf/briar/api/transport/ConnectionWindowFactory.java
@@ -1,6 +1,10 @@
 package net.sf.briar.api.transport;
 
+import java.util.Collection;
+
 public interface ConnectionWindowFactory {
 
-	ConnectionWindow createConnectionWindow(long centre, int bitmap);
+	ConnectionWindow createConnectionWindow();
+
+	ConnectionWindow createConnectionWindow(Collection<Long> unseen);
 }
diff --git a/api/net/sf/briar/api/transport/StreamConnectionFactory.java b/api/net/sf/briar/api/transport/StreamConnectionFactory.java
index 621a072518b594e40a79e0653e84c4efcded2aef..3287453e7e984861a7c812e6a9ba910ac3a4eb04 100644
--- a/api/net/sf/briar/api/transport/StreamConnectionFactory.java
+++ b/api/net/sf/briar/api/transport/StreamConnectionFactory.java
@@ -5,7 +5,7 @@ import net.sf.briar.api.protocol.TransportIndex;
 
 public interface StreamConnectionFactory {
 
-	void createIncomingConnection(TransportIndex i, ContactId c, 
+	void createIncomingConnection(TransportIndex i, ContactId c,
 			StreamTransportConnection s, byte[] encryptedIv);
 
 	void createOutgoingConnection(TransportIndex i, ContactId c,
diff --git a/components/net/sf/briar/crypto/CryptoModule.java b/components/net/sf/briar/crypto/CryptoModule.java
index 221c0bb1307aef949f8fbe206b6ac2d842a7d21a..31dbc46ac7ddf6045240e8f23f61a469de42b767 100644
--- a/components/net/sf/briar/crypto/CryptoModule.java
+++ b/components/net/sf/briar/crypto/CryptoModule.java
@@ -18,6 +18,5 @@ public class CryptoModule extends AbstractModule {
 		// FIXME: Use a real key
 		bind(SecretKey.class).annotatedWith(SecretStorageKey.class).toInstance(
 				new SecretKeySpec(new byte[32], "AES"));
-				
 	}
 }
diff --git a/components/net/sf/briar/db/Database.java b/components/net/sf/briar/db/Database.java
index 39eb9889af59403a7dee2bec971e57215acfa546..60a7b3a80d94037dc665b9d289691890727b1d54 100644
--- a/components/net/sf/briar/db/Database.java
+++ b/components/net/sf/briar/db/Database.java
@@ -28,7 +28,7 @@ import net.sf.briar.api.transport.ConnectionWindow;
  * obtained by calling startTransaction(). Every transaction must be
  * terminated by calling either abortTransaction() or commitTransaction(),
  * even if an exception is thrown.
- * 
+ * <p>
  * Locking is provided by the DatabaseComponent implementation. To prevent
  * deadlock, locks must be acquired in the following order:
  * <ul>
diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java
index e9947db4bf565b0a0fd938401bd60924363778de..fe2172b9651eabb4ba13092477957a558c23927b 100644
--- a/components/net/sf/briar/db/DatabaseComponentImpl.java
+++ b/components/net/sf/briar/db/DatabaseComponentImpl.java
@@ -1597,7 +1597,7 @@ DatabaseCleaner.Callback {
 	}
 
 	/**
-	 * Removes the given message (and all associated state) from the database. 
+	 * Removes the given message (and all associated state) from the database.
 	 * <p>
 	 * Locking: contact read, message write, messageStatus write.
 	 */
diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java
index 58b905b61c13114b4df2f98191fa45a3969d4d10..0ec9015392e60610466fa0607021f38ea183a1b4 100644
--- a/components/net/sf/briar/db/JdbcDatabase.java
+++ b/components/net/sf/briar/db/JdbcDatabase.java
@@ -214,17 +214,24 @@ abstract class JdbcDatabase implements Database<Connection> {
 		+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
 		+ " ON DELETE CASCADE)";
 
-	private static final String CREATE_CONNECTION_WINDOWS =
-		"CREATE TABLE connectionWindows"
+	private static final String CREATE_CONNECTIONS =
+		"CREATE TABLE connections"
 		+ " (contactId INT NOT NULL,"
 		+ " index INT NOT NULL,"
-		+ " centre BIGINT NOT NULL,"
-		+ " bitmap INT NOT NULL,"
 		+ " outgoing BIGINT NOT NULL,"
 		+ " PRIMARY KEY (contactId, index),"
 		+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
 		+ " ON DELETE CASCADE)";
 
+	private static final String CREATE_CONNECTION_WINDOWS =
+		"CREATE TABLE connectionWindows"
+		+ " (contactId INT NOT NULL,"
+		+ " index INT NOT NULL,"
+		+ " unseen BIGINT NOT NULL,"
+		+ " PRIMARY KEY (contactId, index, unseen),"
+		+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
+		+ " ON DELETE CASCADE)";
+
 	private static final String CREATE_SUBSCRIPTION_TIMESTAMPS =
 		"CREATE TABLE subscriptionTimestamps"
 		+ " (contactId INT NOT NULL,"
@@ -342,6 +349,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 			s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_PROPS));
 			s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORTS));
 			s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORT_PROPS));
+			s.executeUpdate(insertTypeNames(CREATE_CONNECTIONS));
 			s.executeUpdate(insertTypeNames(CREATE_CONNECTION_WINDOWS));
 			s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTION_TIMESTAMPS));
 			s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_TIMESTAMPS));
@@ -895,19 +903,19 @@ abstract class JdbcDatabase implements Database<Connection> {
 		PreparedStatement ps = null;
 		ResultSet rs = null;
 		try {
-			String sql = "SELECT outgoing FROM connectionWindows"
+			String sql = "SELECT outgoing FROM connections"
 				+ " WHERE contactId = ? AND index = ?";
 			ps = txn.prepareStatement(sql);
 			ps.setInt(1, c.getInt());
 			ps.setInt(2, i.getInt());
 			rs = ps.executeQuery();
 			if(rs.next()) {
-				// A connection window row exists - update it
+				// A connection row exists - update it
 				long outgoing = rs.getLong(1);
 				if(rs.next()) throw new DbStateException();
 				rs.close();
 				ps.close();
-				sql = "UPDATE connectionWindows SET outgoing = ?"
+				sql = "UPDATE connections SET outgoing = ?"
 					+ " WHERE contactId = ? AND index = ?";
 				ps = txn.prepareStatement(sql);
 				ps.setLong(1, outgoing + 1);
@@ -918,12 +926,11 @@ abstract class JdbcDatabase implements Database<Connection> {
 				ps.close();
 				return outgoing;
 			} else {
-				// No connection window row exists - create one
+				// No connection row exists - create one
 				rs.close();
 				ps.close();
-				sql = "INSERT INTO connectionWindows"
-					+ " (contactId, index, centre, bitmap, outgoing)"
-					+ " VALUES(?, ?, ZERO(), ZERO(), ZERO())";
+				sql = "INSERT INTO connections (contactId, index, outgoing)"
+					+ " VALUES(?, ?, ZERO())";
 				ps = txn.prepareStatement(sql);
 				ps.setInt(1, c.getInt());
 				ps.setInt(2, i.getInt());
@@ -944,23 +951,19 @@ abstract class JdbcDatabase implements Database<Connection> {
 		PreparedStatement ps = null;
 		ResultSet rs = null;
 		try {
-			String sql = "SELECT centre, bitmap FROM connectionWindows"
+			String sql = "SELECT unseen FROM connectionWindows"
 				+ " WHERE contactId = ? AND index = ?";
 			ps = txn.prepareStatement(sql);
 			ps.setInt(1, c.getInt());
 			ps.setInt(2, i.getInt());
 			rs = ps.executeQuery();
-			long centre = 0L;
-			int bitmap = 0;
-			if(rs.next()) {
-				centre = rs.getLong(1);
-				bitmap = rs.getInt(2);
-				if(rs.next()) throw new DbStateException();
-			}
+			Collection<Long> unseen = new ArrayList<Long>();
+			while(rs.next()) unseen.add(rs.getLong(1));
 			rs.close();
 			ps.close();
-			return connectionWindowFactory.createConnectionWindow(centre,
-					bitmap);
+			if(unseen.isEmpty())
+				return connectionWindowFactory.createConnectionWindow();
+			else return connectionWindowFactory.createConnectionWindow(unseen);
 		} catch(SQLException e) {
 			tryToClose(rs);
 			tryToClose(ps);
@@ -2154,46 +2157,34 @@ abstract class JdbcDatabase implements Database<Connection> {
 	public void setConnectionWindow(Connection txn, ContactId c,
 			TransportIndex i, ConnectionWindow w) throws DbException {
 		PreparedStatement ps = null;
-		ResultSet rs = null;
 		try {
-			String sql = "SELECT NULL FROM connectionWindows"
+			// Delete any existing connection window
+			String sql = "DELETE FROM connectionWindows"
 				+ " WHERE contactId = ? AND index = ?";
 			ps = txn.prepareStatement(sql);
 			ps.setInt(1, c.getInt());
 			ps.setInt(2, i.getInt());
-			rs = ps.executeQuery();
-			boolean found = rs.next();
-			if(rs.next()) throw new DbStateException();
-			rs.close();
+			ps.executeUpdate();
 			ps.close();
-			if(found) {
-				// A connection window row exists - update it
-				sql = "UPDATE connectionWindows SET centre = ?, bitmap = ?"
-					+ " WHERE contactId = ? AND index = ?";
-				ps = txn.prepareStatement(sql);
-				ps.setLong(1, w.getCentre());
-				ps.setInt(2, w.getBitmap());
-				ps.setInt(3, c.getInt());
-				ps.setInt(4, i.getInt());
-				int affected = ps.executeUpdate();
-				if(affected != 1) throw new DbStateException();
-				ps.close();
-			} else {
-				// No connection window row exists - create one
-				sql = "INSERT INTO connectionWindows"
-					+ " (contactId, index, centre, bitmap, outgoing)"
-					+ " VALUES(?, ?, ?, ?, ZERO())";
-				ps = txn.prepareStatement(sql);
-				ps.setInt(1, c.getInt());
-				ps.setInt(2, i.getInt());
-				ps.setLong(3, w.getCentre());
-				ps.setInt(4, w.getBitmap());
-				int affected = ps.executeUpdate();
-				if(affected != 1) throw new DbStateException();
-				ps.close();
+			// Store the new connection window
+			sql = "INSERT INTO connectionWindows (contactId, index, unseen)"
+				+ " VALUES(?, ?, ?)";
+			ps = txn.prepareStatement(sql);
+			ps.setInt(1, c.getInt());
+			ps.setInt(2, i.getInt());
+			Collection<Long> unseen = w.getUnseen();
+			for(long l : unseen) {
+				ps.setLong(3, l);
+				ps.addBatch();
 			}
+			int[] affectedBatch = ps.executeBatch();
+			if(affectedBatch.length != unseen.size())
+				throw new DbStateException();
+			for(int j = 0; j < affectedBatch.length; j++) {
+				if(affectedBatch[j] != 1) throw new DbStateException();
+			}
+			ps.close();
 		} catch(SQLException e) {
-			tryToClose(rs);
 			tryToClose(ps);
 			throw new DbException(e);
 		}
diff --git a/components/net/sf/briar/serial/ReaderImpl.java b/components/net/sf/briar/serial/ReaderImpl.java
index 0795d27a4e7db5cc20fb1f2df777a21ed0a273f2..90d5529b7359655277bd300bc503caf18069de06 100644
--- a/components/net/sf/briar/serial/ReaderImpl.java
+++ b/components/net/sf/briar/serial/ReaderImpl.java
@@ -104,7 +104,7 @@ class ReaderImpl implements Reader {
 					objectReaders.length);
 			objectReaders = newObjectReaders;
 		}
-		objectReaders[id] = o;	
+		objectReaders[id] = o;
 	}
 
 	public void removeObjectReader(int id) {
diff --git a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
index 56f728f619ac631f7e37ca188dbad29034431c0e..b82bbec12a7f4f5a1d161e8252db6ca00fa2089f 100644
--- a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
+++ b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
@@ -88,7 +88,7 @@ DatabaseListener {
 	private synchronized void calculateIvs(ContactId c, TransportId t,
 			TransportIndex i, SecretKey ivKey, ConnectionWindow w)
 	throws DbException {
-		for(Long unseen : w.getUnseenConnectionNumbers()) {
+		for(Long unseen : w.getUnseen()) {
 			Bytes iv = new Bytes(encryptIv(i, unseen, ivKey));
 			expected.put(iv, new ConnectionContextImpl(c, t, i, unseen));
 		}
diff --git a/components/net/sf/briar/transport/ConnectionWindowFactoryImpl.java b/components/net/sf/briar/transport/ConnectionWindowFactoryImpl.java
index ff5b3f2fdc9f507eceacb763b84b84b4aac8780a..69a80afd2b27c6e4f68b39c6fad4f9b9c4e10dfe 100644
--- a/components/net/sf/briar/transport/ConnectionWindowFactoryImpl.java
+++ b/components/net/sf/briar/transport/ConnectionWindowFactoryImpl.java
@@ -1,11 +1,17 @@
 package net.sf.briar.transport;
 
+import java.util.Collection;
+
 import net.sf.briar.api.transport.ConnectionWindow;
 import net.sf.briar.api.transport.ConnectionWindowFactory;
 
 class ConnectionWindowFactoryImpl implements ConnectionWindowFactory {
 
-	public ConnectionWindow createConnectionWindow(long centre, int bitmap) {
-		return new ConnectionWindowImpl(centre, bitmap);
+	public ConnectionWindow createConnectionWindow() {
+		return new ConnectionWindowImpl();
+	}
+
+	public ConnectionWindow createConnectionWindow(Collection<Long> unseen) {
+		return new ConnectionWindowImpl(unseen);
 	}
 }
diff --git a/components/net/sf/briar/transport/ConnectionWindowImpl.java b/components/net/sf/briar/transport/ConnectionWindowImpl.java
index f0485cb1acafd2cdc0581c315f6f276d509fff00..3be375edbf8959319355b6a245616208ebdc63cf 100644
--- a/components/net/sf/briar/transport/ConnectionWindowImpl.java
+++ b/components/net/sf/briar/transport/ConnectionWindowImpl.java
@@ -1,69 +1,74 @@
 package net.sf.briar.transport;
 
-import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
+import static net.sf.briar.api.protocol.ProtocolConstants.CONNECTION_WINDOW_SIZE;
 
-import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Set;
+import java.util.TreeSet;
 
 import net.sf.briar.api.transport.ConnectionWindow;
+import net.sf.briar.util.ByteUtils;
 
 class ConnectionWindowImpl implements ConnectionWindow {
 
-	private long centre;
-	private int bitmap;
+	private final Set<Long> unseen;
 
-	ConnectionWindowImpl(long centre, int bitmap) {
-		this.centre = centre;
-		this.bitmap = bitmap;
-	}
+	private long centre;
 
-	public long getCentre() {
-		return centre;
+	ConnectionWindowImpl() {
+		unseen = new TreeSet<Long>();
+		for(long l = 0; l < CONNECTION_WINDOW_SIZE / 2; l++) unseen.add(l);
+		centre = 0;
 	}
 
-	public int getBitmap() {
-		return bitmap;
+	ConnectionWindowImpl(Collection<Long> unseen) {
+		long min = Long.MAX_VALUE, max = Long.MIN_VALUE;
+		for(long l : unseen) {
+			if(l < 0 || l > ByteUtils.MAX_32_BIT_UNSIGNED)
+				throw new IllegalArgumentException();
+			if(l < min) min = l;
+			if(l > max) max = l;
+		}
+		if(max - min > CONNECTION_WINDOW_SIZE)
+			throw new IllegalArgumentException();
+		this.unseen = new TreeSet<Long>(unseen);
+		centre = max - CONNECTION_WINDOW_SIZE / 2 + 1;
+		for(long l = centre; l <= max; l++) {
+			if(!this.unseen.contains(l)) throw new IllegalArgumentException();
+		}
 	}
 
 	public boolean isSeen(long connection) {
-		int offset = getOffset(connection);
-		int mask = 0x80000000 >>> offset;
-		return (bitmap & mask) != 0;
-	}
-
-	private int getOffset(long connection) {
-		if(connection < 0L) throw new IllegalArgumentException();
-		if(connection > MAX_32_BIT_UNSIGNED)
-			throw new IllegalArgumentException();
-		int offset = (int) (connection - centre) + 16;
-		if(offset < 0 || offset > 31) throw new IllegalArgumentException();
-		return offset;
+		return !unseen.contains(connection);
 	}
 
 	public void setSeen(long connection) {
-		int offset = getOffset(connection);
-		int mask = 0x80000000 >>> offset;
-		if((bitmap & mask) != 0) throw new IllegalArgumentException();
-		bitmap |= mask;
-		// If the new connection number is above the centre, slide the window
+		long bottom = getBottom(centre);
+		long top = getTop(centre);
+		if(connection < bottom || connection > top)
+			throw new IllegalArgumentException();
+		if(!unseen.remove(connection)) throw new IllegalArgumentException();
 		if(connection >= centre) {
 			centre = connection + 1;
-			bitmap <<= offset - 16 + 1;
+			long newBottom = getBottom(centre);
+			long newTop = getTop(centre);
+			for(long l = bottom; l < newBottom; l++) unseen.remove(l);
+			for(long l = top + 1; l <= newTop; l++) unseen.add(l);
 		}
 	}
 
-	public Collection<Long> getUnseenConnectionNumbers() {
-		Collection<Long> unseen = new ArrayList<Long>();
-		for(int i = 0; i < 32; i++) {
-			int mask = 0x80000000 >>> i;
-			if((bitmap & mask) == 0) {
-				long c = centre - 16 + i;
-				if(c >= 0L && c <= MAX_32_BIT_UNSIGNED) unseen.add(c);
-			}
-		}
-		// The centre of the window should be an unseen value unless the
-		// maximum possible value has been seen
-		assert unseen.contains(centre) || centre == MAX_32_BIT_UNSIGNED + 1;
+	// Returns the lowest value contained in a window with the given centre
+	private long getBottom(long centre) {
+		return Math.max(0, centre - CONNECTION_WINDOW_SIZE / 2);
+	}
+
+	// Returns the highest value contained in a window with the given centre
+	private long getTop(long centre) {
+		return Math.min(ByteUtils.MAX_32_BIT_UNSIGNED,
+				centre + CONNECTION_WINDOW_SIZE / 2 - 1);
+	}
+
+	public Collection<Long> getUnseen() {
 		return unseen;
 	}
 }
diff --git a/test/net/sf/briar/db/BasicH2Test.java b/test/net/sf/briar/db/BasicH2Test.java
index 843b0519612e465356cf914967eaba187cca162f..f9ddbc93e5f034089ed7be8dc427e01a63bf4752 100644
--- a/test/net/sf/briar/db/BasicH2Test.java
+++ b/test/net/sf/briar/db/BasicH2Test.java
@@ -60,7 +60,7 @@ public class BasicH2Test extends TestCase {
 		// Check that the name can be retrieved using the unique ID
 		assertEquals("foo", getName(uniqueId));
 	}
-	
+
 	private void addRow(byte[] uniqueId, String name) throws SQLException {
 		String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
 		PreparedStatement ps = null;
diff --git a/test/net/sf/briar/db/H2DatabaseTest.java b/test/net/sf/briar/db/H2DatabaseTest.java
index 0d54f31d76c50bd26d406fe17d9d8f915612cf08..9d9471f21b99543d36606a04c5b5509a3f8e0fdf 100644
--- a/test/net/sf/briar/db/H2DatabaseTest.java
+++ b/test/net/sf/briar/db/H2DatabaseTest.java
@@ -33,6 +33,7 @@ import net.sf.briar.api.protocol.GroupFactory;
 import net.sf.briar.api.protocol.GroupId;
 import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.MessageId;
+import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Transport;
 import net.sf.briar.api.protocol.TransportId;
 import net.sf.briar.api.protocol.TransportIndex;
@@ -1446,8 +1447,8 @@ public class H2DatabaseTest extends TestCase {
 				remoteIndex);
 		// The connection window should exist and be in the initial state
 		assertNotNull(w);
-		assertEquals(0L, w.getCentre());
-		assertEquals(0, w.getBitmap());
+		long top = ProtocolConstants.CONNECTION_WINDOW_SIZE / 2 - 1;
+		for(long l = 0; l <= top; l++) assertFalse(w.isSeen(l));
 
 		db.commitTransaction(txn);
 		db.close();
@@ -1465,17 +1466,21 @@ public class H2DatabaseTest extends TestCase {
 				remoteIndex);
 		// The connection window should exist and be in the initial state
 		assertNotNull(w);
-		assertEquals(0L, w.getCentre());
-		assertEquals(0, w.getBitmap());
+		Collection<Long> unseen = w.getUnseen();
+		long top = ProtocolConstants.CONNECTION_WINDOW_SIZE / 2 - 1;
+		assertEquals(top + 1, unseen.size());
+		for(long l = 0; l <= top; l++) {
+			assertFalse(w.isSeen(l));
+			assertTrue(unseen.contains(l));
+		}
 		// Update the connection window and store it
-		w.setSeen(5L);
+		w.setSeen(5);
 		db.setConnectionWindow(txn, contactId, remoteIndex, w);
 		// Check that the connection window was stored
 		w = db.getConnectionWindow(txn, contactId, remoteIndex);
 		assertNotNull(w);
-		assertEquals(6L, w.getCentre());
-		assertTrue(w.isSeen(5L));
-		assertEquals(0x00010000, w.getBitmap());
+		top += 5;
+		for(long l = 0; l <= top; l++) assertEquals(l == 5, w.isSeen(l));
 
 		db.commitTransaction(txn);
 		db.close();
diff --git a/test/net/sf/briar/protocol/ConsumersTest.java b/test/net/sf/briar/protocol/ConsumersTest.java
index 9605c1c403daaabe83b84eb6f67383ee1ef0158b..4c09535a779c9562d1ef0fd55e74c4e16adb726e 100644
--- a/test/net/sf/briar/protocol/ConsumersTest.java
+++ b/test/net/sf/briar/protocol/ConsumersTest.java
@@ -28,7 +28,7 @@ public class ConsumersTest extends TestCase {
 		Injector i = Guice.createInjector(new CryptoModule());
 		crypto = i.getInstance(CryptoComponent.class);
 	}
-		
+
 	@Test
 	public void testDigestingConsumer() throws Exception {
 		MessageDigest messageDigest = crypto.getMessageDigest();
diff --git a/test/net/sf/briar/serial/WriterImplTest.java b/test/net/sf/briar/serial/WriterImplTest.java
index 7fe9b285072144c97dad2a30b367d690715b2404..72b2eace7bff74edc88eeff5b7021903535dff74 100644
--- a/test/net/sf/briar/serial/WriterImplTest.java
+++ b/test/net/sf/briar/serial/WriterImplTest.java
@@ -101,7 +101,7 @@ public class WriterImplTest extends TestCase {
 	@Test
 	public void testWriteFloat32() throws IOException {
 		// http://babbage.cs.qc.edu/IEEE-754/Decimal.html
-		// 1 bit for sign, 8 for exponent, 23 for significand 
+		// 1 bit for sign, 8 for exponent, 23 for significand
 		w.writeFloat32(0F); // 0 0 0 -> 0x00000000
 		w.writeFloat32(1F); // 0 127 1 -> 0x3F800000
 		w.writeFloat32(2F); // 0 128 1 -> 0x40000000
@@ -118,7 +118,7 @@ public class WriterImplTest extends TestCase {
 
 	@Test
 	public void testWriteFloat64() throws IOException {
-		// 1 bit for sign, 11 for exponent, 52 for significand 
+		// 1 bit for sign, 11 for exponent, 52 for significand
 		w.writeFloat64(0.0); // 0 0 0 -> 0x0000000000000000
 		w.writeFloat64(1.0); // 0 1023 1 -> 0x3FF0000000000000
 		w.writeFloat64(2.0); // 0 1024 1 -> 0x4000000000000000
diff --git a/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java b/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java
index 20361bac56f93cdf7492eb290efcf760a4fcb82c..369ad8c5e0c84daa4b93af3df3af0aceed66d0ac 100644
--- a/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java
@@ -84,7 +84,7 @@ public class ConnectionDecrypterImplTest extends TestCase {
 		out.write(ciphertextMac);
 		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
 		// Use a ConnectionDecrypter to decrypt the ciphertext
-		ConnectionDecrypter d = new ConnectionDecrypterImpl(in, 
+		ConnectionDecrypter d = new ConnectionDecrypterImpl(in,
 				IvEncoder.encodeIv(initiator, transportIndex, connection),
 				frameCipher, frameKey);
 		// First frame
diff --git a/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java b/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java
index 2400c2dceb4f4d3977317b0446463371e8d40ede..e01d7e2518cebdbe41016a8bb4803cf31a4a076a 100644
--- a/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java
@@ -49,7 +49,7 @@ public class ConnectionRecogniserImplTest extends TestCase {
 		Transport transport = new Transport(transportId, localIndex,
 				Collections.singletonMap("foo", "bar"));
 		transports = Collections.singletonList(transport);
-		connectionWindow = new ConnectionWindowImpl(0L, 0);
+		connectionWindow = new ConnectionWindowImpl();
 	}
 
 	@Test
@@ -120,8 +120,7 @@ public class ConnectionRecogniserImplTest extends TestCase {
 		// Second time - the IV should no longer be expected
 		assertNull(c.acceptConnection(encryptedIv));
 		// The window should have advanced
-		assertEquals(4L, connectionWindow.getCentre());
-		Collection<Long> unseen = connectionWindow.getUnseenConnectionNumbers();
+		Collection<Long> unseen = connectionWindow.getUnseen();
 		assertEquals(19, unseen.size());
 		for(int i = 0; i < 19; i++) {
 			if(i == 3) continue;
diff --git a/test/net/sf/briar/transport/ConnectionWindowImplTest.java b/test/net/sf/briar/transport/ConnectionWindowImplTest.java
index 54595b2342b66152afdae866daf4ab3071096baf..d39855f911276ca79500d9a09c23bd77c78ad050 100644
--- a/test/net/sf/briar/transport/ConnectionWindowImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionWindowImplTest.java
@@ -1,18 +1,18 @@
 package net.sf.briar.transport;
 
+import java.util.ArrayList;
 import java.util.Collection;
 
 import junit.framework.TestCase;
+import net.sf.briar.util.ByteUtils;
 
 import org.junit.Test;
 
 public class ConnectionWindowImplTest extends TestCase {
 
-	private static final long MAX_32_BIT_UNSIGNED = 4294967295L; // 2^32 - 1
-
 	@Test
 	public void testWindowSliding() {
-		ConnectionWindowImpl w = new ConnectionWindowImpl(0L, 0);
+		ConnectionWindowImpl w = new ConnectionWindowImpl();
 		for(int i = 0; i < 100; i++) {
 			assertFalse(w.isSeen(i));
 			w.setSeen(i);
@@ -22,7 +22,7 @@ public class ConnectionWindowImplTest extends TestCase {
 
 	@Test
 	public void testWindowJumping() {
-		ConnectionWindowImpl w = new ConnectionWindowImpl(0L, 0);
+		ConnectionWindowImpl w = new ConnectionWindowImpl();
 		for(int i = 0; i < 100; i += 13) {
 			assertFalse(w.isSeen(i));
 			w.setSeen(i);
@@ -32,7 +32,7 @@ public class ConnectionWindowImplTest extends TestCase {
 
 	@Test
 	public void testWindowUpperLimit() {
-		ConnectionWindowImpl w = new ConnectionWindowImpl(0L, 0);
+		ConnectionWindowImpl w = new ConnectionWindowImpl();
 		// Centre is 0, highest value in window is 15
 		w.setSeen(15);
 		// Centre is 16, highest value in window is 31
@@ -42,18 +42,22 @@ public class ConnectionWindowImplTest extends TestCase {
 			w.setSeen(48);
 			fail();
 		} catch(IllegalArgumentException expected) {}
-		w = new ConnectionWindowImpl(MAX_32_BIT_UNSIGNED - 1, 0);
-		// Values greater than 2^31 - 1 should never be allowed
-		w.setSeen(MAX_32_BIT_UNSIGNED);
+		// Values greater than 2^32 - 1 should never be allowed
+		Collection<Long> unseen = new ArrayList<Long>();
+		for(int i = 0; i < 32; i++) {
+			unseen.add(ByteUtils.MAX_32_BIT_UNSIGNED - i);
+		}
+		w = new ConnectionWindowImpl(unseen);
+		w.setSeen(ByteUtils.MAX_32_BIT_UNSIGNED);
 		try {
-			w.setSeen(MAX_32_BIT_UNSIGNED + 1);
+			w.setSeen(ByteUtils.MAX_32_BIT_UNSIGNED + 1);
 			fail();
 		} catch(IllegalArgumentException expected) {}
 	}
 
 	@Test
 	public void testWindowLowerLimit() {
-		ConnectionWindowImpl w = new ConnectionWindowImpl(0L, 0);
+		ConnectionWindowImpl w = new ConnectionWindowImpl();
 		// Centre is 0, negative values should never be allowed
 		try {
 			w.setSeen(-1);
@@ -83,7 +87,7 @@ public class ConnectionWindowImplTest extends TestCase {
 
 	@Test
 	public void testCannotSetSeenTwice() {
-		ConnectionWindowImpl w = new ConnectionWindowImpl(0L, 0);
+		ConnectionWindowImpl w = new ConnectionWindowImpl();
 		w.setSeen(15);
 		try {
 			w.setSeen(15);
@@ -93,9 +97,9 @@ public class ConnectionWindowImplTest extends TestCase {
 
 	@Test
 	public void testGetUnseenConnectionNumbers() {
-		ConnectionWindowImpl w = new ConnectionWindowImpl(0L, 0);
+		ConnectionWindowImpl w = new ConnectionWindowImpl();
 		// Centre is 0; window should cover 0 to 15, inclusive, with none seen
-		Collection<Long> unseen = w.getUnseenConnectionNumbers();
+		Collection<Long> unseen = w.getUnseen();
 		assertEquals(16, unseen.size());
 		for(int i = 0; i < 16; i++) {
 			assertTrue(unseen.contains(Long.valueOf(i)));
@@ -104,7 +108,7 @@ public class ConnectionWindowImplTest extends TestCase {
 		w.setSeen(3);
 		w.setSeen(4);
 		// Centre is 5; window should cover 0 to 20, inclusive, with two seen
-		unseen = w.getUnseenConnectionNumbers();
+		unseen = w.getUnseen();
 		assertEquals(19, unseen.size());
 		for(int i = 0; i < 21; i++) {
 			if(i == 3 || i == 4) {
@@ -117,7 +121,7 @@ public class ConnectionWindowImplTest extends TestCase {
 		}
 		w.setSeen(19);
 		// Centre is 20; window should cover 4 to 35, inclusive, with two seen
-		unseen = w.getUnseenConnectionNumbers();
+		unseen = w.getUnseen();
 		assertEquals(30, unseen.size());
 		for(int i = 4; i < 36; i++) {
 			if(i == 4 || i == 19) {