From 72e9a9d807c30313bcef630a8dbba498d2559a4c Mon Sep 17 00:00:00 2001
From: Torsten Grote <t@grobox.de>
Date: Wed, 25 Apr 2018 10:12:49 -0300
Subject: [PATCH] Address first round of review comments for new
 IntroductionClient

---
 .../event/IntroductionSucceededEvent.java     |  1 -
 .../briar/client/BdfIncomingMessageHook.java  | 15 ++--
 .../introduction/AbstractProtocolEngine.java  | 25 ------
 .../IntroduceeProtocolEngine.java             | 88 ++++++++++--------
 .../IntroducerProtocolEngine.java             | 57 +++---------
 .../introduction/IntroductionConstants.java   |  1 -
 .../introduction/IntroductionCrypto.java      | 17 ++--
 .../introduction/IntroductionCryptoImpl.java  | 89 +++++++++++--------
 .../introduction/IntroductionManagerImpl.java | 85 +++++++++++-------
 .../introduction/IntroductionValidator.java   |  3 +-
 .../briar/introduction/MessageEncoder.java    |  4 +-
 .../introduction/MessageEncoderImpl.java      | 10 +--
 .../briar/introduction/MessageMetadata.java   |  9 +-
 .../briar/introduction/MessageParser.java     |  2 +-
 .../briar/introduction/MessageParserImpl.java |  6 +-
 .../IntroductionValidatorTest.java            |  2 +-
 .../MessageEncoderParserIntegrationTest.java  |  5 +-
 17 files changed, 194 insertions(+), 225 deletions(-)

diff --git a/briar-api/src/main/java/org/briarproject/briar/api/introduction/event/IntroductionSucceededEvent.java b/briar-api/src/main/java/org/briarproject/briar/api/introduction/event/IntroductionSucceededEvent.java
index 44d166863e..88998789c2 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/introduction/event/IntroductionSucceededEvent.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/introduction/event/IntroductionSucceededEvent.java
@@ -8,7 +8,6 @@ import javax.annotation.concurrent.Immutable;
 
 @Immutable
 @NotNullByDefault
-// TODO still needed?
 public class IntroductionSucceededEvent extends Event {
 
 	private final Contact contact;
diff --git a/briar-core/src/main/java/org/briarproject/briar/client/BdfIncomingMessageHook.java b/briar-core/src/main/java/org/briarproject/briar/client/BdfIncomingMessageHook.java
index 17f02ca43f..bf83fb6a28 100644
--- a/briar-core/src/main/java/org/briarproject/briar/client/BdfIncomingMessageHook.java
+++ b/briar-core/src/main/java/org/briarproject/briar/client/BdfIncomingMessageHook.java
@@ -57,19 +57,14 @@ public abstract class BdfIncomingMessageHook implements IncomingMessageHook {
 	public boolean incomingMessage(Transaction txn, Message m, Metadata meta)
 			throws DbException, InvalidMessageException {
 		try {
-			return incomingMessage(txn, m, meta, MESSAGE_HEADER_LENGTH);
+			byte[] raw = m.getRaw();
+			BdfList body = clientHelper.toList(raw, MESSAGE_HEADER_LENGTH,
+					raw.length - MESSAGE_HEADER_LENGTH);
+			BdfDictionary metaDictionary = metadataParser.parse(meta);
+			return incomingMessage(txn, m, body, metaDictionary);
 		} catch (FormatException e) {
 			throw new InvalidMessageException(e);
 		}
 	}
 
-	private boolean incomingMessage(Transaction txn, Message m, Metadata meta,
-			int headerLength) throws DbException, FormatException {
-		byte[] raw = m.getRaw();
-		BdfList body = clientHelper.toList(raw, headerLength,
-				raw.length - headerLength);
-		BdfDictionary metaDictionary = metadataParser.parse(meta);
-		return incomingMessage(txn, m, body, metaDictionary);
-	}
-
 }
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java
index 20ac593078..300d3f6fb1 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java
@@ -13,7 +13,6 @@ import org.briarproject.bramble.api.identity.IdentityManager;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.plugin.TransportId;
 import org.briarproject.bramble.api.properties.TransportProperties;
-import org.briarproject.bramble.api.sync.Group;
 import org.briarproject.bramble.api.sync.Message;
 import org.briarproject.bramble.api.sync.MessageId;
 import org.briarproject.bramble.api.system.Clock;
@@ -25,8 +24,6 @@ import java.util.Map;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.Immutable;
 
-import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
-import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_VERSION;
 import static org.briarproject.briar.introduction.MessageType.ABORT;
 import static org.briarproject.briar.introduction.MessageType.ACCEPT;
 import static org.briarproject.briar.introduction.MessageType.ACTIVATE;
@@ -155,28 +152,6 @@ abstract class AbstractProtocolEngine<S extends Session>
 		}
 	}
 
-	void markRequestUnavailableToAnswer(Transaction txn, MessageId m)
-			throws DbException {
-		BdfDictionary meta = new BdfDictionary();
-		messageEncoder.setAvailableToAnswer(meta, false);
-		try {
-			clientHelper.mergeMessageMetadata(txn, m, meta);
-		} catch (FormatException e) {
-			throw new AssertionError(e);
-		}
-	}
-
-	Map<MessageId, BdfDictionary> getSessions(Transaction txn,
-			BdfDictionary query) throws DbException, FormatException {
-		return clientHelper
-				.getMessageMetadataAsDictionary(txn, getLocalGroup().getId(),
-						query);
-	}
-
-	private Group getLocalGroup() {
-		return contactGroupFactory.createLocalGroup(CLIENT_ID, CLIENT_VERSION);
-	}
-
 	boolean isInvalidDependency(@Nullable MessageId lastRemoteMessageId,
 			@Nullable MessageId dependency) {
 		if (dependency == null) return lastRemoteMessageId != null;
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
index e463dd39a7..8bb5781eb5 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
@@ -124,8 +124,7 @@ class IntroduceeProtocolEngine
 
 	@Override
 	public IntroduceeSession onRequestMessage(Transaction txn,
-			IntroduceeSession session, RequestMessage m)
-			throws DbException, FormatException {
+			IntroduceeSession session, RequestMessage m) throws DbException {
 		switch (session.getState()) {
 			case START:
 				return onRemoteRequest(txn, session, m);
@@ -143,8 +142,7 @@ class IntroduceeProtocolEngine
 
 	@Override
 	public IntroduceeSession onAcceptMessage(Transaction txn,
-			IntroduceeSession session, AcceptMessage m)
-			throws DbException, FormatException {
+			IntroduceeSession session, AcceptMessage m) throws DbException {
 		switch (session.getState()) {
 			case START:
 				return onRemoteResponseInStart(txn, session, m);
@@ -163,8 +161,7 @@ class IntroduceeProtocolEngine
 
 	@Override
 	public IntroduceeSession onDeclineMessage(Transaction txn,
-			IntroduceeSession session, DeclineMessage m)
-			throws DbException, FormatException {
+			IntroduceeSession session, DeclineMessage m) throws DbException {
 		switch (session.getState()) {
 			case START:
 				return onRemoteResponseInStart(txn, session, m);
@@ -183,8 +180,7 @@ class IntroduceeProtocolEngine
 
 	@Override
 	public IntroduceeSession onAuthMessage(Transaction txn,
-			IntroduceeSession session, AuthMessage m)
-			throws DbException, FormatException {
+			IntroduceeSession session, AuthMessage m) throws DbException {
 		switch (session.getState()) {
 			case AWAIT_AUTH:
 				return onRemoteAuth(txn, session, m);
@@ -202,8 +198,7 @@ class IntroduceeProtocolEngine
 
 	@Override
 	public IntroduceeSession onActivateMessage(Transaction txn,
-			IntroduceeSession session, ActivateMessage m)
-			throws DbException, FormatException {
+			IntroduceeSession session, ActivateMessage m) throws DbException {
 		switch (session.getState()) {
 			case AWAIT_ACTIVATE:
 				return onRemoteActivate(txn, session, m);
@@ -221,8 +216,7 @@ class IntroduceeProtocolEngine
 
 	@Override
 	public IntroduceeSession onAbortMessage(Transaction txn,
-			IntroduceeSession session, AbortMessage m)
-			throws DbException, FormatException {
+			IntroduceeSession session, AbortMessage m) throws DbException {
 		return onRemoteAbort(txn, session, m);
 	}
 
@@ -232,8 +226,9 @@ class IntroduceeProtocolEngine
 		if (isInvalidDependency(s, m.getPreviousMessageId()))
 			return abort(txn, s);
 
-		// Mark the request visible in the UI
+		// Mark the request visible in the UI and available to answer
 		markMessageVisibleInUi(txn, m.getMessageId());
+		markRequestAvailableToAnswer(txn, m.getMessageId(), true);
 
 		// Add SessionId to message metadata
 		addSessionId(txn, m.getMessageId(), s.getSessionId());
@@ -243,9 +238,11 @@ class IntroduceeProtocolEngine
 				.trackMessage(txn, m.getGroupId(), m.getTimestamp(), false);
 
 		// Broadcast IntroductionRequestReceivedEvent
+		LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
 		Contact c = contactManager.getContact(txn, s.getIntroducer().getId(),
-				identityManager.getLocalAuthor(txn).getId());
-		boolean contactExists = false; // TODO
+				localAuthor.getId());
+		boolean contactExists = contactManager
+				.contactExists(txn, m.getAuthor().getId(), localAuthor.getId());
 		IntroductionRequest request =
 				new IntroductionRequest(s.getSessionId(), m.getMessageId(),
 						m.getGroupId(), INTRODUCEE, m.getTimestamp(), false,
@@ -260,11 +257,10 @@ class IntroduceeProtocolEngine
 	}
 
 	private IntroduceeSession onLocalAccept(Transaction txn,
-			IntroduceeSession s, long timestamp) throws DbException {
+			IntroduceeSession s, long timestamp)
+			throws DbException {
 		// Mark the request message unavailable to answer
-		MessageId requestId = s.getLastRemoteMessageId();
-		if (requestId == null) throw new IllegalStateException();
-		markRequestUnavailableToAnswer(txn, requestId);
+		markRequestsUnavailableToAnswer(txn, s);
 
 		// Create ephemeral key pair and get local transport properties
 		KeyPair keyPair = crypto.generateKeyPair();
@@ -275,7 +271,7 @@ class IntroduceeProtocolEngine
 
 		// Send a ACCEPT message
 		long localTimestamp =
-				Math.max(timestamp, getLocalTimestamp(s));
+				Math.max(timestamp + 1, getLocalTimestamp(s));
 		Message sent = sendAcceptMessage(txn, s, localTimestamp, publicKey,
 				localTimestamp, transportProperties, true);
 		// Track the message
@@ -297,14 +293,13 @@ class IntroduceeProtocolEngine
 	}
 
 	private IntroduceeSession onLocalDecline(Transaction txn,
-			IntroduceeSession s, long timestamp) throws DbException {
+			IntroduceeSession s, long timestamp)
+			throws DbException {
 		// Mark the request message unavailable to answer
-		MessageId requestId = s.getLastRemoteMessageId();
-		if (requestId == null) throw new IllegalStateException();
-		markRequestUnavailableToAnswer(txn, requestId);
+		markRequestsUnavailableToAnswer(txn, s);
 
 		// Send a DECLINE message
-		long localTimestamp = Math.max(timestamp, getLocalTimestamp(s));
+		long localTimestamp = Math.max(timestamp + 1, getLocalTimestamp(s));
 		Message sent = sendDeclineMessage(txn, s, localTimestamp, true);
 		// Track the message
 		messageTracker.trackOutgoingMessage(txn, sent);
@@ -316,7 +311,7 @@ class IntroduceeProtocolEngine
 
 	private IntroduceeSession onRemoteAccept(Transaction txn,
 			IntroduceeSession s, AcceptMessage m)
-			throws DbException, FormatException {
+			throws DbException {
 		// The timestamp must be higher than the last request message
 		if (m.getTimestamp() <= s.getRequestTimestamp())
 			return abort(txn, s);
@@ -346,7 +341,7 @@ class IntroduceeProtocolEngine
 		if (isInvalidDependency(s, m.getPreviousMessageId()))
 			return abort(txn, s);
 
-		// Mark the request visible in the UI
+		// Mark the response visible in the UI
 		markMessageVisibleInUi(txn, m.getMessageId());
 
 		// Track the incoming message
@@ -401,8 +396,6 @@ class IntroduceeProtocolEngine
 		} catch (GeneralSecurityException e) {
 			// TODO
 			return abort(txn, s);
-		} catch (FormatException e) {
-			throw new AssertionError(e);
 		}
 		if (s.getState() != AWAIT_AUTH) throw new AssertionError();
 		Message sent = sendAuthMessage(txn, s, getLocalTimestamp(s), mac,
@@ -411,8 +404,7 @@ class IntroduceeProtocolEngine
 	}
 
 	private IntroduceeSession onRemoteAuth(Transaction txn,
-			IntroduceeSession s, AuthMessage m)
-			throws DbException, FormatException {
+			IntroduceeSession s, AuthMessage m) throws DbException {
 		// The dependency, if any, must be the last remote message
 		if (isInvalidDependency(s, m.getPreviousMessageId()))
 			return abort(txn, s);
@@ -479,9 +471,7 @@ class IntroduceeProtocolEngine
 			IntroduceeSession s, AbortMessage m)
 			throws DbException {
 		// Mark the request message unavailable to answer
-		MessageId requestId = s.getLastRemoteMessageId();
-		if (requestId == null) throw new IllegalStateException();
-		markRequestUnavailableToAnswer(txn, requestId);
+		markRequestsUnavailableToAnswer(txn, s);
 
 		// Broadcast abort event for testing
 		txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
@@ -495,9 +485,7 @@ class IntroduceeProtocolEngine
 	private IntroduceeSession abort(Transaction txn, IntroduceeSession s)
 			throws DbException {
 		// Mark the request message unavailable to answer
-		MessageId requestId = s.getLastRemoteMessageId();
-		if (requestId == null) throw new IllegalStateException();
-		markRequestUnavailableToAnswer(txn, requestId);
+		markRequestsUnavailableToAnswer(txn, s);
 
 		// Send an ABORT message
 		Message sent = sendAbortMessage(txn, s, getLocalTimestamp(s));
@@ -537,4 +525,30 @@ class IntroduceeProtocolEngine
 		}
 	}
 
+	private void markRequestsUnavailableToAnswer(Transaction txn,
+			IntroduceeSession s) throws DbException {
+		BdfDictionary query = messageParser
+				.getRequestsAvailableToAnswerQuery(s.getSessionId());
+		try {
+			Map<MessageId, BdfDictionary> results =
+					clientHelper.getMessageMetadataAsDictionary(txn,
+							s.getContactGroupId(), query);
+			for (MessageId m : results.keySet())
+				markRequestAvailableToAnswer(txn, m, false);
+		} catch (FormatException e) {
+			throw new AssertionError(e);
+		}
+	}
+
+	private void markRequestAvailableToAnswer(Transaction txn, MessageId m,
+			boolean available) throws DbException {
+		BdfDictionary meta = new BdfDictionary();
+		messageEncoder.setAvailableToAnswer(meta, available);
+		try {
+			clientHelper.mergeMessageMetadata(txn, m, meta);
+		} catch (FormatException e) {
+			throw new AssertionError(e);
+		}
+	}
+
 }
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java
index 044b7ccd2c..98ad2fe53c 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java
@@ -1,11 +1,9 @@
 package org.briarproject.briar.introduction;
 
-import org.briarproject.bramble.api.FormatException;
 import org.briarproject.bramble.api.client.ClientHelper;
 import org.briarproject.bramble.api.client.ContactGroupFactory;
 import org.briarproject.bramble.api.contact.Contact;
 import org.briarproject.bramble.api.contact.ContactManager;
-import org.briarproject.bramble.api.data.BdfDictionary;
 import org.briarproject.bramble.api.db.DatabaseComponent;
 import org.briarproject.bramble.api.db.DbException;
 import org.briarproject.bramble.api.db.Transaction;
@@ -23,8 +21,6 @@ import org.briarproject.briar.api.introduction.event.IntroductionAbortedEvent;
 import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent;
 import org.briarproject.briar.introduction.IntroducerSession.Introducee;
 
-import java.util.Map;
-
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.Immutable;
 import javax.inject.Inject;
@@ -97,21 +93,19 @@ class IntroducerProtocolEngine
 	}
 
 	IntroducerSession onAbortAction(Transaction txn, IntroducerSession s)
-			throws DbException, FormatException {
+			throws DbException {
 		return abort(txn, s);
 	}
 
 	@Override
 	public IntroducerSession onRequestMessage(Transaction txn,
-			IntroducerSession s, RequestMessage m)
-			throws DbException, FormatException {
+			IntroducerSession s, RequestMessage m) throws DbException {
 		return abort(txn, s); // Invalid in this role
 	}
 
 	@Override
 	public IntroducerSession onAcceptMessage(Transaction txn,
-			IntroducerSession s, AcceptMessage m)
-			throws DbException, FormatException {
+			IntroducerSession s, AcceptMessage m) throws DbException {
 		switch (s.getState()) {
 			case AWAIT_RESPONSES:
 			case AWAIT_RESPONSE_A:
@@ -133,8 +127,7 @@ class IntroducerProtocolEngine
 
 	@Override
 	public IntroducerSession onDeclineMessage(Transaction txn,
-			IntroducerSession s, DeclineMessage m)
-			throws DbException, FormatException {
+			IntroducerSession s, DeclineMessage m) throws DbException {
 		switch (s.getState()) {
 			case AWAIT_RESPONSES:
 			case AWAIT_RESPONSE_A:
@@ -156,7 +149,7 @@ class IntroducerProtocolEngine
 
 	@Override
 	public IntroducerSession onAuthMessage(Transaction txn, IntroducerSession s,
-			AuthMessage m) throws DbException, FormatException {
+			AuthMessage m) throws DbException {
 		switch (s.getState()) {
 			case AWAIT_AUTHS:
 			case AWAIT_AUTH_A:
@@ -177,8 +170,7 @@ class IntroducerProtocolEngine
 
 	@Override
 	public IntroducerSession onActivateMessage(Transaction txn,
-			IntroducerSession s, ActivateMessage m)
-			throws DbException, FormatException {
+			IntroducerSession s, ActivateMessage m) throws DbException {
 		switch (s.getState()) {
 			case AWAIT_ACTIVATES:
 			case AWAIT_ACTIVATE_A:
@@ -199,8 +191,7 @@ class IntroducerProtocolEngine
 
 	@Override
 	public IntroducerSession onAbortMessage(Transaction txn,
-			IntroducerSession s, AbortMessage m)
-			throws DbException, FormatException {
+			IntroducerSession s, AbortMessage m) throws DbException {
 		return onRemoteAbort(txn, s, m);
 	}
 
@@ -227,8 +218,7 @@ class IntroducerProtocolEngine
 	}
 
 	private IntroducerSession onRemoteAccept(Transaction txn,
-			IntroducerSession s, AcceptMessage m)
-			throws DbException, FormatException {
+			IntroducerSession s, AcceptMessage m) throws DbException {
 		// The timestamp must be higher than the last request message
 		if (m.getTimestamp() <= s.getRequestTimestamp())
 			return abort(txn, s);
@@ -284,8 +274,7 @@ class IntroducerProtocolEngine
 	}
 
 	private IntroducerSession onRemoteDecline(Transaction txn,
-			IntroducerSession s, DeclineMessage m)
-			throws DbException, FormatException {
+			IntroducerSession s, DeclineMessage m) throws DbException {
 		// The timestamp must be higher than the last request message
 		if (m.getTimestamp() <= s.getRequestTimestamp())
 			return abort(txn, s);
@@ -337,7 +326,7 @@ class IntroducerProtocolEngine
 
 	private IntroducerSession onRemoteResponseInStart(Transaction txn,
 			IntroducerSession s, AbstractIntroductionMessage m)
-			throws DbException, FormatException {
+			throws DbException {
 		// The timestamp must be higher than the last request message
 		if (m.getTimestamp() <= s.getRequestTimestamp())
 			return abort(txn, s);
@@ -384,8 +373,7 @@ class IntroducerProtocolEngine
 	}
 
 	private IntroducerSession onRemoteAuth(Transaction txn,
-			IntroducerSession s, AuthMessage m)
-			throws DbException, FormatException {
+			IntroducerSession s, AuthMessage m) throws DbException {
 		// The dependency, if any, must be the last remote message
 		if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId()))
 			return abort(txn, s);
@@ -413,8 +401,7 @@ class IntroducerProtocolEngine
 	}
 
 	private IntroducerSession onRemoteActivate(Transaction txn,
-			IntroducerSession s, ActivateMessage m)
-			throws DbException, FormatException {
+			IntroducerSession s, ActivateMessage m) throws DbException {
 		// The dependency, if any, must be the last remote message
 		if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId()))
 			return abort(txn, s);
@@ -441,11 +428,7 @@ class IntroducerProtocolEngine
 	}
 
 	private IntroducerSession onRemoteAbort(Transaction txn,
-			IntroducerSession s, AbortMessage m)
-			throws DbException, FormatException {
-		// Mark any REQUEST messages in the session unavailable to answer
-		markRequestsUnavailableToAnswer(txn, s);
-
+			IntroducerSession s, AbortMessage m) throws DbException {
 		// Forward ABORT message
 		Introducee i = getOtherIntroducee(s, m.getGroupId());
 		long timestamp = getLocalTimestamp(s, i);
@@ -468,10 +451,7 @@ class IntroducerProtocolEngine
 	}
 
 	private IntroducerSession abort(Transaction txn,
-			IntroducerSession s) throws DbException, FormatException {
-		// Mark any REQUEST messages in the session unavailable to answer
-		markRequestsUnavailableToAnswer(txn, s);
-
+			IntroducerSession s) throws DbException {
 		// Broadcast abort event for testing
 		txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
 
@@ -487,15 +467,6 @@ class IntroducerProtocolEngine
 				s.getRequestTimestamp(), introducee1, introducee2);
 	}
 
-	private void markRequestsUnavailableToAnswer(Transaction txn, Session s)
-			throws DbException, FormatException {
-		BdfDictionary query = messageParser
-				.getInvitesAvailableToAnswerQuery(s.getSessionId());
-		Map<MessageId, BdfDictionary> results = getSessions(txn, query);
-		for (MessageId m : results.keySet())
-			markRequestUnavailableToAnswer(txn, m);
-	}
-
 	private Introducee getIntroducee(IntroducerSession s, GroupId g) {
 		if (s.getIntroducee1().groupId.equals(g)) return s.getIntroducee1();
 		else if (s.getIntroducee2().groupId.equals(g))
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionConstants.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionConstants.java
index bda50f47b9..af93fa955e 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionConstants.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionConstants.java
@@ -12,7 +12,6 @@ interface IntroductionConstants {
 	String MSG_KEY_LOCAL = "local";
 	String MSG_KEY_VISIBLE_IN_UI = "visibleInUi";
 	String MSG_KEY_AVAILABLE_TO_ANSWER = "availableToAnswer";
-	String MSG_KEY_INVITATION_ACCEPTED = "invitationAccepted";
 
 	// Session Keys
 	String SESSION_KEY_SESSION_ID = "sessionId";
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCrypto.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCrypto.java
index a563e3c753..3573f348a7 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCrypto.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCrypto.java
@@ -1,6 +1,5 @@
 package org.briarproject.briar.introduction;
 
-import org.briarproject.bramble.api.FormatException;
 import org.briarproject.bramble.api.crypto.KeyPair;
 import org.briarproject.bramble.api.crypto.SecretKey;
 import org.briarproject.bramble.api.identity.Author;
@@ -15,15 +14,13 @@ interface IntroductionCrypto {
 	/**
 	 * Returns the {@link SessionId} based on the introducer
 	 * and the two introducees.
-	 *
-	 * Note: The roles of Alice and Bob can be switched.
 	 */
-	SessionId getSessionId(Author introducer, Author alice, Author bob);
+	SessionId getSessionId(Author introducer, Author local, Author remote);
 
 	/**
-	 * Returns true if the first author is indeed alice
+	 * Returns true if the local author is alice
 	 */
-	boolean isAlice(AuthorId alice, AuthorId bob);
+	boolean isAlice(AuthorId local, AuthorId remote);
 
 	/**
 	 * Generates an agreement key pair.
@@ -49,11 +46,11 @@ interface IntroductionCrypto {
 	SecretKey deriveMacKey(SecretKey masterKey, boolean alice);
 
 	/**
-	 * Generates a MAC that covers both introducee's ephemeral public keys and
-	 * transport properties.
+	 * Generates a MAC that covers both introducee's ephemeral public keys,
+	 * transport properties, Author IDs and timestamps of the accept message.
 	 */
 	byte[] mac(SecretKey macKey, IntroduceeSession s, AuthorId localAuthorId,
-			boolean alice) throws FormatException;
+			boolean alice);
 
 	/**
 	 * Verifies a received MAC
@@ -63,7 +60,7 @@ interface IntroductionCrypto {
 	 * @throws GeneralSecurityException if the verification fails
 	 */
 	void verifyMac(byte[] mac, IntroduceeSession s, AuthorId localAuthorId)
-			throws GeneralSecurityException, FormatException;
+			throws GeneralSecurityException;
 
 	/**
 	 * Signs a nonce derived from the macKey
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCryptoImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCryptoImpl.java
index 9bf4e46c2b..18e2cbe120 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCryptoImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCryptoImpl.java
@@ -18,7 +18,6 @@ import org.briarproject.bramble.api.properties.TransportProperties;
 import org.briarproject.briar.api.client.SessionId;
 
 import java.security.GeneralSecurityException;
-import java.util.Arrays;
 import java.util.Map;
 
 import javax.annotation.concurrent.Immutable;
@@ -49,14 +48,14 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
 	}
 
 	@Override
-	public SessionId getSessionId(Author introducer, Author alice,
-			Author bob) {
-		boolean isAlice = isAlice(alice.getId(), bob.getId());
+	public SessionId getSessionId(Author introducer, Author local,
+			Author remote) {
+		boolean isAlice = isAlice(local.getId(), remote.getId());
 		byte[] hash = crypto.hash(
 				LABEL_SESSION_ID,
 				introducer.getId().getBytes(),
-				isAlice ? alice.getId().getBytes() : bob.getId().getBytes(),
-				isAlice ? bob.getId().getBytes() : alice.getId().getBytes()
+				isAlice ? local.getId().getBytes() : remote.getId().getBytes(),
+				isAlice ? remote.getId().getBytes() : local.getId().getBytes()
 		);
 		return new SessionId(hash);
 	}
@@ -67,9 +66,9 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
 	}
 
 	@Override
-	public boolean isAlice(AuthorId alice, AuthorId bob) {
-		byte[] a = alice.getBytes();
-		byte[] b = bob.getBytes();
+	public boolean isAlice(AuthorId local, AuthorId remote) {
+		byte[] a = local.getBytes();
+		byte[] b = remote.getBytes();
 		return Bytes.COMPARATOR.compare(new Bytes(a), new Bytes(b)) < 0;
 	}
 
@@ -110,7 +109,7 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
 	@Override
 	@SuppressWarnings("ConstantConditions")
 	public byte[] mac(SecretKey macKey, IntroduceeSession s,
-			AuthorId localAuthorId, boolean alice) throws FormatException {
+			AuthorId localAuthorId, boolean alice) {
 		return mac(macKey, s.getIntroducer().getId(), localAuthorId,
 				s.getRemoteAuthor().getId(), s.getAcceptTimestamp(),
 				s.getRemoteAcceptTimestamp(), s.getEphemeralPublicKey(),
@@ -124,28 +123,16 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
 			byte[] ephemeralPublicKey, byte[] remoteEphemeralPublicKey,
 			Map<TransportId, TransportProperties> transportProperties,
 			Map<TransportId, TransportProperties> remoteTransportProperties,
-			boolean alice) throws FormatException {
-		BdfList localInfo = BdfList.of(
-				localAuthorId,
-				acceptTimestamp,
-				ephemeralPublicKey,
-				clientHelper.toDictionary(transportProperties)
-		);
-		BdfList remoteInfo = BdfList.of(
-				remoteAuthorId,
-				remoteAcceptTimestamp,
-				remoteEphemeralPublicKey,
-				clientHelper.toDictionary(remoteTransportProperties)
-		);
-		BdfList macList = BdfList.of(
-				introducerId,
-				alice ? localInfo : remoteInfo,
-				alice ? remoteInfo : localInfo
-		);
+			boolean alice) {
+		byte[] inputs =
+				getMacInputs(introducerId, localAuthorId, remoteAuthorId,
+						acceptTimestamp, remoteAcceptTimestamp,
+						ephemeralPublicKey, remoteEphemeralPublicKey,
+						transportProperties, remoteTransportProperties, alice);
 		return crypto.mac(
 				LABEL_AUTH_MAC,
 				macKey,
-				clientHelper.toByteArray(macList)
+				inputs
 		);
 	}
 
@@ -153,7 +140,7 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
 	@SuppressWarnings("ConstantConditions")
 	public void verifyMac(byte[] mac, IntroduceeSession s,
 			AuthorId localAuthorId)
-			throws GeneralSecurityException, FormatException {
+			throws GeneralSecurityException {
 		boolean alice = isAlice(localAuthorId, s.getRemoteAuthor().getId());
 		verifyMac(mac, new SecretKey(s.getMasterKey()),
 				s.getIntroducer().getId(), localAuthorId,
@@ -170,18 +157,49 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
 			byte[] remoteEphemeralPublicKey,
 			Map<TransportId, TransportProperties> transportProperties,
 			Map<TransportId, TransportProperties> remoteTransportProperties,
-			boolean alice) throws GeneralSecurityException, FormatException {
+			boolean alice) throws GeneralSecurityException {
 		SecretKey macKey = deriveMacKey(masterKey, alice);
-		byte[] calculatedMac =
-				mac(macKey, introducerId, localAuthorId, remoteAuthorId,
+		byte[] inputs =
+				getMacInputs(introducerId, localAuthorId, remoteAuthorId,
 						acceptTimestamp, remoteAcceptTimestamp,
 						ephemeralPublicKey, remoteEphemeralPublicKey,
 						transportProperties, remoteTransportProperties, !alice);
-		if (!Arrays.equals(mac, calculatedMac)) {
+		if (!crypto.verifyMac(mac, LABEL_AUTH_MAC, macKey, inputs)) {
 			throw new GeneralSecurityException();
 		}
 	}
 
+	private byte[] getMacInputs(AuthorId introducerId,
+			AuthorId localAuthorId, AuthorId remoteAuthorId,
+			long acceptTimestamp, long remoteAcceptTimestamp,
+			byte[] ephemeralPublicKey, byte[] remoteEphemeralPublicKey,
+			Map<TransportId, TransportProperties> transportProperties,
+			Map<TransportId, TransportProperties> remoteTransportProperties,
+			boolean alice) {
+		BdfList localInfo = BdfList.of(
+				localAuthorId,
+				acceptTimestamp,
+				ephemeralPublicKey,
+				clientHelper.toDictionary(transportProperties)
+		);
+		BdfList remoteInfo = BdfList.of(
+				remoteAuthorId,
+				remoteAcceptTimestamp,
+				remoteEphemeralPublicKey,
+				clientHelper.toDictionary(remoteTransportProperties)
+		);
+		BdfList macList = BdfList.of(
+				introducerId,
+				alice ? localInfo : remoteInfo,
+				alice ? remoteInfo : localInfo
+		);
+		try {
+			return clientHelper.toByteArray(macList);
+		} catch (FormatException e) {
+			throw new AssertionError();
+		}
+	}
+
 	@Override
 	public byte[] sign(SecretKey macKey, byte[] privateKey)
 			throws GeneralSecurityException {
@@ -204,7 +222,8 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
 	void verifySignature(SecretKey macKey, byte[] publicKey,
 			byte[] signature) throws GeneralSecurityException {
 		byte[] nonce = getNonce(macKey);
-		if (!crypto.verify(LABEL_AUTH_SIGN, nonce, publicKey, signature)) {
+		if (!crypto.verifySignature(signature, LABEL_AUTH_SIGN, nonce,
+				publicKey)) {
 			throw new GeneralSecurityException();
 		}
 	}
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java
index 7b7f9cd9b3..0e4639f7fb 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.client.ClientHelper;
 import org.briarproject.bramble.api.client.ContactGroupFactory;
 import org.briarproject.bramble.api.contact.Contact;
 import org.briarproject.bramble.api.contact.ContactId;
+import org.briarproject.bramble.api.contact.ContactManager;
 import org.briarproject.bramble.api.contact.ContactManager.ContactHook;
 import org.briarproject.bramble.api.data.BdfDictionary;
 import org.briarproject.bramble.api.data.BdfList;
@@ -37,6 +38,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.Immutable;
@@ -59,6 +61,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
 		implements IntroductionManager, Client, ContactHook {
 
 	private final ContactGroupFactory contactGroupFactory;
+	private final ContactManager contactManager;
 	private final MessageParser messageParser;
 	private final SessionEncoder sessionEncoder;
 	private final SessionParser sessionParser;
@@ -74,6 +77,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
 			MetadataParser metadataParser,
 			MessageTracker messageTracker,
 			ContactGroupFactory contactGroupFactory,
+			ContactManager contactManager,
 			MessageParser messageParser,
 			SessionEncoder sessionEncoder,
 			SessionParser sessionParser,
@@ -83,6 +87,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
 			IdentityManager identityManager) {
 		super(db, clientHelper, metadataParser, messageTracker);
 		this.contactGroupFactory = contactGroupFactory;
+		this.contactManager = contactManager;
 		this.messageParser = messageParser;
 		this.sessionEncoder = sessionEncoder;
 		this.sessionParser = sessionParser;
@@ -107,8 +112,6 @@ class IntroductionManagerImpl extends ConversationClientImpl
 	public void addingContact(Transaction txn, Contact c) throws DbException {
 		// Create a group to share with the contact
 		Group g = getContactGroup(c);
-		// Return if we've already set things up for this contact
-		if (db.containsGroup(txn, g.getId())) return;
 		// Store the group and share it with the contact
 		db.addGroup(txn, g);
 		db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
@@ -124,12 +127,9 @@ class IntroductionManagerImpl extends ConversationClientImpl
 
 	@Override
 	public void removingContact(Transaction txn, Contact c) throws DbException {
-		try {
-			removeSessionWithIntroducer(txn, c);
-			abortOrRemoveSessionWithIntroducee(txn, c);
-		} catch (FormatException e) {
-			throw new AssertionError();
-		}
+		removeSessionWithIntroducer(txn, c);
+		abortOrRemoveSessionWithIntroducee(txn, c);
+
 		// Remove the contact group (all messages will be removed with it)
 		db.removeGroup(txn, getContactGroup(c));
 	}
@@ -185,12 +185,12 @@ class IntroductionManagerImpl extends ConversationClientImpl
 			Message m, BdfList body) throws DbException, FormatException {
 		ContactId introducerId = getContactId(txn, m.getGroupId());
 		Author introducer = db.getContact(txn, introducerId).getAuthor();
-		Author alice = identityManager.getLocalAuthor(txn);
-		Author bob = messageParser.parseRequestMessage(m, body).getAuthor();
-		if (alice.equals(bob)) throw new FormatException();
-		SessionId sessionId = crypto.getSessionId(introducer, alice, bob);
+		Author local = identityManager.getLocalAuthor(txn);
+		Author remote = messageParser.parseRequestMessage(m, body).getAuthor();
+		if (local.equals(remote)) throw new FormatException();
+		SessionId sessionId = crypto.getSessionId(introducer, local, remote);
 		return IntroduceeSession
-				.getInitial(m.getGroupId(), sessionId, introducer, bob);
+				.getInitial(m.getGroupId(), sessionId, introducer, remote);
 	}
 
 	private <S extends Session> S handleMessage(Transaction txn, Message m,
@@ -249,7 +249,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
 	}
 
 	private void storeSession(Transaction txn, MessageId storageId,
-			Session session) throws DbException, FormatException {
+			Session session) throws DbException {
 		BdfDictionary d;
 		if (session.getRole() == INTRODUCER) {
 			d = sessionEncoder
@@ -260,7 +260,11 @@ class IntroductionManagerImpl extends ConversationClientImpl
 		} else {
 			throw new AssertionError();
 		}
-		clientHelper.mergeMessageMetadata(txn, storageId, d);
+		try {
+			clientHelper.mergeMessageMetadata(txn, storageId, d);
+		} catch (FormatException e) {
+			throw new AssertionError();
+		}
 	}
 
 	@Override
@@ -356,7 +360,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
 			Map<MessageId, BdfDictionary> results = clientHelper
 					.getMessageMetadataAsDictionary(txn, contactGroupId, query);
 			messages = new ArrayList<>(results.size());
-			for (Map.Entry<MessageId, BdfDictionary> e : results.entrySet()) {
+			for (Entry<MessageId, BdfDictionary> e : results.entrySet()) {
 				MessageId m = e.getKey();
 				MessageMetadata meta =
 						messageParser.parseMetadata(e.getValue());
@@ -394,11 +398,11 @@ class IntroductionManagerImpl extends ConversationClientImpl
 		Role role = sessionParser.getRole(bdfSession);
 		SessionId sessionId;
 		Author author;
+		LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
 		if (role == INTRODUCER) {
 			IntroducerSession session =
 					sessionParser.parseIntroducerSession(bdfSession);
 			sessionId = session.getSessionId();
-			LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
 			if (localAuthor.equals(session.getIntroducee1().author)) {
 				author = session.getIntroducee2().author;
 			} else {
@@ -410,8 +414,14 @@ class IntroductionManagerImpl extends ConversationClientImpl
 			sessionId = session.getSessionId();
 			author = session.getRemoteAuthor();
 		} else throw new AssertionError();
-		String message = ""; // TODO
-		boolean contactExists = false; // TODO
+		Message msg = clientHelper.getMessage(txn, m);
+		BdfList body = clientHelper.getMessageAsList(txn, m);
+		if (msg == null || body == null) throw new AssertionError();
+		RequestMessage rm = messageParser.parseRequestMessage(msg, body);
+		String message = rm.getMessage();
+		boolean contactExists = contactManager
+				.contactExists(txn, rm.getAuthor().getId(),
+						localAuthor.getId());
 
 		return new IntroductionRequest(sessionId, m, contactGroupId,
 				role, meta.getTimestamp(), meta.isLocal(),
@@ -449,12 +459,16 @@ class IntroductionManagerImpl extends ConversationClientImpl
 	}
 
 	private void removeSessionWithIntroducer(Transaction txn,
-			Contact introducer) throws DbException, FormatException {
+			Contact introducer) throws DbException {
 		BdfDictionary query = sessionEncoder
 				.getIntroduceeSessionsByIntroducerQuery(introducer.getAuthor());
-		Map<MessageId, BdfDictionary> sessions = clientHelper
-				.getMessageMetadataAsDictionary(txn, getLocalGroup().getId(),
-						query);
+		Map<MessageId, BdfDictionary> sessions;
+		try {
+			sessions = clientHelper.getMessageMetadataAsDictionary(txn,
+					getLocalGroup().getId(), query);
+		} catch (FormatException e) {
+			throw new AssertionError(e);
+		}
 		for (MessageId id : sessions.keySet()) {
 			db.deleteMessageMetadata(txn, id); // TODO needed?
 			db.removeMessage(txn, id);
@@ -462,16 +476,23 @@ class IntroductionManagerImpl extends ConversationClientImpl
 	}
 
 	private void abortOrRemoveSessionWithIntroducee(Transaction txn,
-			Contact c) throws DbException, FormatException {
+			Contact c) throws DbException {
 		BdfDictionary query = sessionEncoder.getIntroducerSessionsQuery();
-		Map<MessageId, BdfDictionary> sessions = clientHelper
-				.getMessageMetadataAsDictionary(txn, getLocalGroup().getId(),
-						query);
+		Map<MessageId, BdfDictionary> sessions;
+		try {
+			sessions = clientHelper.getMessageMetadataAsDictionary(txn,
+					getLocalGroup().getId(), query);
+		} catch (FormatException e) {
+			throw new AssertionError();
+		}
 		LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
-		for (Map.Entry<MessageId, BdfDictionary> session : sessions
-				.entrySet()) {
-			IntroducerSession s =
-					sessionParser.parseIntroducerSession(session.getValue());
+		for (Entry<MessageId, BdfDictionary> session : sessions.entrySet()) {
+			IntroducerSession s;
+			try {
+				s = sessionParser.parseIntroducerSession(session.getValue());
+			} catch (FormatException e) {
+				throw new AssertionError();
+			}
 			if (s.getIntroducee1().author.equals(c.getAuthor())) {
 				abortOrRemoveSessionWithIntroducee(txn, s, session.getKey(),
 						s.getIntroducee2(), localAuthor);
@@ -484,7 +505,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
 
 	private void abortOrRemoveSessionWithIntroducee(Transaction txn,
 			IntroducerSession s, MessageId storageId, Introducee i,
-			LocalAuthor localAuthor) throws DbException, FormatException {
+			LocalAuthor localAuthor) throws DbException {
 		if (db.containsContact(txn, i.author.getId(), localAuthor.getId())) {
 			IntroducerSession session = introducerEngine.onAbortAction(txn, s);
 			storeSession(txn, storageId, session);
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionValidator.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionValidator.java
index 16d26afdf3..43027b40b1 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionValidator.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionValidator.java
@@ -78,8 +78,7 @@ class IntroductionValidator extends BdfMessageValidator {
 		checkLength(msg, 1, MAX_REQUEST_MESSAGE_LENGTH);
 
 		BdfDictionary meta = messageEncoder
-				.encodeRequestMetadata(m.getTimestamp(), false, false,
-						false, false);
+				.encodeRequestMetadata(m.getTimestamp(), false, false, false);
 		if (previousMessageId == null) {
 			return new BdfMessageContext(meta);
 		} else {
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoder.java b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoder.java
index d619e3d76e..42eb41c0f4 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoder.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoder.java
@@ -18,7 +18,7 @@ import javax.annotation.Nullable;
 interface MessageEncoder {
 
 	BdfDictionary encodeRequestMetadata(long timestamp, boolean local,
-			boolean read, boolean available, boolean accepted);
+			boolean read, boolean available);
 
 	BdfDictionary encodeMetadata(MessageType type,
 			@Nullable SessionId sessionId, long timestamp, boolean local,
@@ -30,8 +30,6 @@ interface MessageEncoder {
 
 	void setAvailableToAnswer(BdfDictionary meta, boolean available);
 
-	void setInvitationAccepted(BdfDictionary meta, boolean accepted);
-
 	Message encodeRequestMessage(GroupId contactGroupId, long timestamp,
 			@Nullable MessageId previousMessageId, Author author,
 			@Nullable String message);
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoderImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoderImpl.java
index 5bade9d86f..861569ac5e 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoderImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoderImpl.java
@@ -21,7 +21,6 @@ import javax.inject.Inject;
 
 import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
 import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_AVAILABLE_TO_ANSWER;
-import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_INVITATION_ACCEPTED;
 import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_LOCAL;
 import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_MESSAGE_TYPE;
 import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_SESSION_ID;
@@ -49,12 +48,10 @@ class MessageEncoderImpl implements MessageEncoder {
 
 	@Override
 	public BdfDictionary encodeRequestMetadata(long timestamp,
-			boolean local, boolean read, boolean available,
-			boolean accepted) {
+			boolean local, boolean read, boolean available) {
 		BdfDictionary meta =
 				encodeMetadata(REQUEST, null, timestamp, local, read, false);
 		meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, available);
-		meta.put(MSG_KEY_INVITATION_ACCEPTED, accepted);
 		return meta;
 	}
 
@@ -90,11 +87,6 @@ class MessageEncoderImpl implements MessageEncoder {
 		meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, available);
 	}
 
-	@Override
-	public void setInvitationAccepted(BdfDictionary meta, boolean accepted) {
-		meta.put(MSG_KEY_INVITATION_ACCEPTED, accepted);
-	}
-
 	@Override
 	public Message encodeRequestMessage(GroupId contactGroupId, long timestamp,
 			@Nullable MessageId previousMessageId, Author author,
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageMetadata.java b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageMetadata.java
index 9b3ea54b53..102d72bfc3 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageMetadata.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageMetadata.java
@@ -14,11 +14,11 @@ class MessageMetadata {
 	@Nullable
 	private final SessionId sessionId;
 	private final long timestamp;
-	private final boolean local, read, visible, available, accepted;
+	private final boolean local, read, visible, available;
 
 	MessageMetadata(MessageType type, @Nullable SessionId sessionId,
 			long timestamp, boolean local, boolean read, boolean visible,
-			boolean available, boolean accepted) {
+			boolean available) {
 		this.type = type;
 		this.sessionId = sessionId;
 		this.timestamp = timestamp;
@@ -26,7 +26,6 @@ class MessageMetadata {
 		this.read = read;
 		this.visible = visible;
 		this.available = available;
-		this.accepted = accepted;
 	}
 
 	MessageType getMessageType() {
@@ -58,8 +57,4 @@ class MessageMetadata {
 		return available;
 	}
 
-	public boolean wasAccepted() {
-		return accepted;
-	}
-
 }
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageParser.java b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageParser.java
index 58f9dbfabd..503dd4cc66 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageParser.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageParser.java
@@ -12,7 +12,7 @@ interface MessageParser {
 
 	BdfDictionary getMessagesVisibleInUiQuery();
 
-	BdfDictionary getInvitesAvailableToAnswerQuery(SessionId sessionId);
+	BdfDictionary getRequestsAvailableToAnswerQuery(SessionId sessionId);
 
 	MessageMetadata parseMetadata(BdfDictionary meta) throws FormatException;
 
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageParserImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageParserImpl.java
index 263af78442..ef97282dd8 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageParserImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageParserImpl.java
@@ -19,7 +19,6 @@ import javax.inject.Inject;
 
 import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
 import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_AVAILABLE_TO_ANSWER;
-import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_INVITATION_ACCEPTED;
 import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_LOCAL;
 import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_MESSAGE_TYPE;
 import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_SESSION_ID;
@@ -43,7 +42,7 @@ class MessageParserImpl implements MessageParser {
 	}
 
 	@Override
-	public BdfDictionary getInvitesAvailableToAnswerQuery(SessionId sessionId) {
+	public BdfDictionary getRequestsAvailableToAnswerQuery(SessionId sessionId) {
 		return BdfDictionary.of(
 				new BdfEntry(MSG_KEY_AVAILABLE_TO_ANSWER, true),
 				new BdfEntry(MSG_KEY_MESSAGE_TYPE, REQUEST.getValue()),
@@ -64,9 +63,8 @@ class MessageParserImpl implements MessageParser {
 		boolean read = d.getBoolean(MSG_KEY_READ);
 		boolean visible = d.getBoolean(MSG_KEY_VISIBLE_IN_UI);
 		boolean available = d.getBoolean(MSG_KEY_AVAILABLE_TO_ANSWER, false);
-		boolean accepted = d.getBoolean(MSG_KEY_INVITATION_ACCEPTED, false);
 		return new MessageMetadata(type, sessionId, timestamp, local, read,
-				visible, available, accepted);
+				visible, available);
 	}
 
 	@Override
diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionValidatorTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionValidatorTest.java
index 4629a1f733..fbebeece40 100644
--- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionValidatorTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionValidatorTest.java
@@ -397,7 +397,7 @@ public class IntroductionValidatorTest extends ValidatorTestCase {
 		context.checking(new Expectations() {{
 			oneOf(messageEncoder)
 					.encodeRequestMetadata(message.getTimestamp(), false, false,
-							false, false);
+							false);
 			will(returnValue(meta));
 		}});
 	}
diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/MessageEncoderParserIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/MessageEncoderParserIntegrationTest.java
index f8c8fae348..83d4611221 100644
--- a/briar-core/src/test/java/org/briarproject/briar/introduction/MessageEncoderParserIntegrationTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/introduction/MessageEncoderParserIntegrationTest.java
@@ -74,8 +74,7 @@ public class MessageEncoderParserIntegrationTest extends BrambleTestCase {
 	@Test
 	public void testRequestMessageMetadata() throws FormatException {
 		BdfDictionary d = messageEncoder
-				.encodeRequestMetadata(timestamp, true, false, false,
-						true);
+				.encodeRequestMetadata(timestamp, true, false, false);
 		MessageMetadata meta = messageParser.parseMetadata(d);
 
 		assertEquals(REQUEST, meta.getMessageType());
@@ -85,7 +84,6 @@ public class MessageEncoderParserIntegrationTest extends BrambleTestCase {
 		assertFalse(meta.isRead());
 		assertFalse(meta.isVisibleInConversation());
 		assertFalse(meta.isAvailableToAnswer());
-		assertTrue(meta.wasAccepted());
 	}
 
 	@Test
@@ -102,7 +100,6 @@ public class MessageEncoderParserIntegrationTest extends BrambleTestCase {
 		assertTrue(meta.isRead());
 		assertFalse(meta.isVisibleInConversation());
 		assertFalse(meta.isAvailableToAnswer());
-		assertFalse(meta.wasAccepted());
 	}
 
 	@Test
-- 
GitLab