diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java
index c852b359a7d962e64397ad1b06608847f9cd2ed5..5237ea4ac214bb1bf00ebbfac23d9200765ffca1 100644
--- a/router/java/src/net/i2p/router/transport/TransportManager.java
+++ b/router/java/src/net/i2p/router/transport/TransportManager.java
@@ -94,12 +94,6 @@ public class TransportManager implements TransportEventListener {
     private static final String PROP_JAVA_PROXY3 = "http.proxyHost";
     private static final String PROP_JAVA_PROXY4 = "https.proxyHost";
 
-    /** default true */
-    private static final String PROP_NTCP1_ENABLE = "i2np.ntcp1.enable";
-    private static final boolean DEFAULT_NTCP1_ENABLE = false;
-    private static final String PROP_NTCP2_ENABLE = "i2np.ntcp2.enable";
-    private static final boolean DEFAULT_NTCP2_ENABLE = true;
-
     private static final String PROP_ADVANCED = "routerconsole.advanced";
     
     /** not forever, since they may update */
@@ -125,10 +119,8 @@ public class TransportManager implements TransportEventListener {
         _upnpManager = enableUPnP ? new UPnPManager(context, this) : null;
         _upnpRefresher = enableUPnP ? new UPnPRefresher() : null;
         _enableUDP = _context.getBooleanPropertyDefaultTrue(PROP_ENABLE_UDP);
-        _enableNTCP1 = isNTCPEnabled(context) &&
-                       context.getProperty(PROP_NTCP1_ENABLE, DEFAULT_NTCP1_ENABLE);
-        boolean enableNTCP2 = isNTCPEnabled(context) &&
-                              context.getProperty(PROP_NTCP2_ENABLE, DEFAULT_NTCP2_ENABLE);
+        _enableNTCP1 = false;
+        boolean enableNTCP2 = isNTCPEnabled(context);
         _dhThread = (_enableUDP || enableNTCP2) ? new DHSessionKeyBuilder.PrecalcRunner(context) : null;
         // always created, even if NTCP2 is not enabled, because ratchet needs it
         _xdhThread = new X25519KeyFactory(context);
@@ -268,10 +260,7 @@ public class TransportManager implements TransportEventListener {
             initializeAddress(udp);
         }
         if (isNTCPEnabled(_context)) {
-            DHSessionKeyBuilder.PrecalcRunner dh = _enableNTCP1 ? _dhThread : null;
-            boolean enableNTCP2 = _context.getProperty(PROP_NTCP2_ENABLE, DEFAULT_NTCP2_ENABLE);
-            X25519KeyFactory xdh = enableNTCP2 ? _xdhThread : null;
-            Transport ntcp = new NTCPTransport(_context, dh, xdh);
+            Transport ntcp = new NTCPTransport(_context, null, _xdhThread);
             addTransport(ntcp);
             initializeAddress(ntcp);
             if (udp != null) {
@@ -292,9 +281,7 @@ public class TransportManager implements TransportEventListener {
     }
     
     public static boolean isNTCPEnabled(RouterContext ctx) {
-        return ctx.getBooleanPropertyDefaultTrue(PROP_ENABLE_NTCP) &&
-               (ctx.getProperty(PROP_NTCP1_ENABLE, DEFAULT_NTCP1_ENABLE) ||
-                ctx.getProperty(PROP_NTCP2_ENABLE, DEFAULT_NTCP2_ENABLE));
+        return ctx.getBooleanPropertyDefaultTrue(PROP_ENABLE_NTCP);
     }
     
     /**
diff --git a/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java b/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java
index 88e1cd068c4ce10481036ef0dc4af98a7035b957..8e47a6bf7151da6da53c7120eb6b1e39dfdc6e44 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java
@@ -164,8 +164,8 @@ class InboundEstablishState extends EstablishBase implements NTCP2Payload.Payloa
                         _log.warn("Short buffer got " + remaining + " total now " + _received + " on " + this);
                     return;
                 }
-                if (remaining + _received < NTCP1_MSG1_SIZE ||
-                    !_transport.isNTCP1Enabled()) {
+                //if (remaining + _received < NTCP1_MSG1_SIZE ||
+                //    !_transport.isNTCP1Enabled()) {
                     // Less than 288 total received, assume NTCP2
                     // TODO can't change our mind later if we get more than 287
                     _con.setVersion(2);
@@ -173,343 +173,8 @@ class InboundEstablishState extends EstablishBase implements NTCP2Payload.Payloa
                     receiveInboundNTCP2(src);
                     // releaseBufs() will return the unused DH
                     return;
-                }
-            }
-            int toGet = Math.min(remaining, XY_SIZE - _received);
-            src.get(_X, _received, toGet);
-            _received += toGet;
-            if (_received < XY_SIZE)
-                return;
-            changeState(State.IB_GOT_X);
-            _received = 0;
-        }
-
-        if (_state == State.IB_GOT_X && src.hasRemaining()) {
-            int toGet = Math.min(src.remaining(), HXY_SIZE - _received);
-            src.get(_hX_xor_bobIdentHash, _received, toGet);
-            _received += toGet;
-            if (_received < HXY_SIZE)
-                return;
-            changeState(State.IB_GOT_HX);
-            _received = 0;
-        }
-
-        if (_state == State.IB_GOT_HX) {
-
-                if (_log.shouldLog(Log.DEBUG))
-                    _log.debug(prefix()+"Enough data for a DH received");
-
-                // first verify that Alice knows who she is trying to talk with and that the X
-                // isn't corrupt
-                byte[] realXor = SimpleByteCache.acquire(HXY_SIZE);
-                _context.sha().calculateHash(_X, 0, XY_SIZE, realXor, 0);
-                xor32(_context.routerHash().getData(), realXor);
-                if (!DataHelper.eq(realXor, _hX_xor_bobIdentHash)) {
-                    SimpleByteCache.release(realXor);
-                    _context.statManager().addRateData("ntcp.invalidHXxorBIH", 1);
-                    fail("Invalid hX_xor");
-                    return;
-                }
-                SimpleByteCache.release(realXor);
-                if (!_transport.isHXHIValid(_hX_xor_bobIdentHash)) {
-                    // blocklist source? but spoofed IPs could DoS us
-                    _context.statManager().addRateData("ntcp.replayHXxorBIH", 1);
-                    fail("Replay hX_xor");
-                    return;
-                }
-
-                try {
-                    // ok, they're actually trying to talk to us, and we got their (unauthenticated) X
-                    _dh.setPeerPublicValue(_X);
-                    _dh.getSessionKey(); // force the calc
-                    System.arraycopy(_hX_xor_bobIdentHash, AES_SIZE, _prevEncrypted, 0, AES_SIZE);
-                    if (_log.shouldLog(Log.DEBUG))
-                        _log.debug(prefix()+"DH session key calculated (" + _dh.getSessionKey().toBase64() + ")");
-
-                    // now prepare our response: Y+E(H(X+Y)+tsB+padding, sk, Y[239:255])
-                    byte xy[] = new byte[XY_SIZE + XY_SIZE];
-                    System.arraycopy(_X, 0, xy, 0, XY_SIZE);
-                    System.arraycopy(_Y, 0, xy, XY_SIZE, XY_SIZE);
-                    byte[] hxy = SimpleByteCache.acquire(HXY_SIZE);
-                    _context.sha().calculateHash(xy, 0, XY_SIZE + XY_SIZE, hxy, 0);
-                    // our (Bob's) timestamp in seconds
-                    _tsB = (_context.clock().now() + 500) / 1000l;
-                    byte toEncrypt[] = new byte[HXY_TSB_PAD_SIZE];  // 48
-                    System.arraycopy(hxy, 0, toEncrypt, 0, HXY_SIZE);
-                    byte tsB[] = DataHelper.toLong(4, _tsB);
-                    System.arraycopy(tsB, 0, toEncrypt, HXY_SIZE, tsB.length);
-                    _context.random().nextBytes(toEncrypt, HXY_SIZE + 4, 12);
-                    if (_log.shouldLog(Log.DEBUG)) {
-                        _log.debug(prefix()+"h(x+y)="+Base64.encode(hxy));
-                        _log.debug(prefix() + "tsb = " + _tsB);
-                        _log.debug(prefix()+"unencrypted H(X+Y)+tsB+padding: " + Base64.encode(toEncrypt));
-                        _log.debug(prefix()+"encryption iv= " + Base64.encode(_Y, XY_SIZE-AES_SIZE, AES_SIZE));
-                        _log.debug(prefix()+"encryption key= " + _dh.getSessionKey().toBase64());
-                    }
-                    SimpleByteCache.release(hxy);
-                    _context.aes().encrypt(toEncrypt, 0, _e_hXY_tsB, 0, _dh.getSessionKey(),
-                                           _Y, XY_SIZE-AES_SIZE, HXY_TSB_PAD_SIZE);
-                    if (_log.shouldLog(Log.DEBUG))
-                        _log.debug(prefix()+"encrypted H(X+Y)+tsB+padding: " + Base64.encode(_e_hXY_tsB));
-                    byte write[] = new byte[XY_SIZE + HXY_TSB_PAD_SIZE];
-                    System.arraycopy(_Y, 0, write, 0, XY_SIZE);
-                    System.arraycopy(_e_hXY_tsB, 0, write, XY_SIZE, HXY_TSB_PAD_SIZE);
-
-                    // ok, now that is prepared, we want to actually send it, so make sure we are up for writing
-                    changeState(State.IB_SENT_Y);
-                    _transport.getPumper().wantsWrite(_con, write);
-                    if (!src.hasRemaining()) return;
-                } catch (DHSessionKeyBuilder.InvalidPublicParameterException e) {
-                    _context.statManager().addRateData("ntcp.invalidDH", 1);
-                    fail("Invalid X", e);
-                    return;
-                } catch (IllegalStateException ise) {
-                    // setPeerPublicValue()
-                    fail("reused keys?", ise);
-                    return;
-                }
-
-        }
-
-        // ok, we are onto the encrypted area, i.e. Message #3
-        while (STATES_MSG3.contains(_state) && src.hasRemaining()) {
-
-                // Collect a 16-byte block
-                if (_received < AES_SIZE && src.hasRemaining()) {
-                    int toGet = Math.min(src.remaining(), AES_SIZE - _received);
-                    src.get(_curEncrypted, _received, toGet);
-                    _received += toGet;
-                    if (_received < AES_SIZE) {
-                        // no more bytes available in the buffer, and only a partial
-                        // block was read, so we can't decrypt it.
-                        if (_log.shouldLog(Log.DEBUG))
-                            _log.debug(prefix() + "end of available data with only a partial block read (" +
-                                       + _received + ")");
-                        return;
-                    }
-                }
-                // Decrypt the 16-byte block
-                if (_received >= AES_SIZE) {
-                    _context.aes().decrypt(_curEncrypted, 0, _curDecrypted, 0, _dh.getSessionKey(),
-                                           _prevEncrypted, 0, AES_SIZE);
-
-                    byte swap[] = _prevEncrypted;
-                    _prevEncrypted = _curEncrypted;
-                    _curEncrypted = swap;
-                    _received = 0;
-
-                    if (_state == State.IB_SENT_Y) { // we are on the first decrypted block
-                        int sz = (int)DataHelper.fromLong(_curDecrypted, 0, 2);
-                        if (sz < MIN_RI_SIZE || sz > MAX_RI_SIZE) {
-                            _context.statManager().addRateData("ntcp.invalidInboundSize", sz);
-                            fail("size is invalid", new Exception("size is " + sz));
-                            return;
-                        }
-                        if (_log.shouldLog(Log.DEBUG))
-                            _log.debug(prefix() + "got the RI size: " + sz);
-                        _aliceIdentSize  = sz;
-                        changeState(State.IB_GOT_RI_SIZE);
-
-                        // We must defer the calculations for total size of the message until
-                        //  we get the full alice ident so
-                        // we can determine how long the signature is.
-                        // See below
-
-                    }
-                    try {
-                        _sz_aliceIdent_tsA_padding_aliceSig.write(_curDecrypted);
-                    } catch (IOException ioe) {
-                        if (_log.shouldLog(Log.ERROR)) _log.error(prefix()+"Error writing to the baos?", ioe);
-                    }
-
-                    if (_state == State.IB_GOT_RI_SIZE &&
-                        _sz_aliceIdent_tsA_padding_aliceSig.size() >= 2 + _aliceIdentSize) {
-                        // we have enough to get Alice's RI and determine the sig+padding length
-                        readAliceRouterIdentity();
-                        if (_log.shouldLog(Log.DEBUG))
-                            _log.debug(prefix() + "got the RI");
-                        if (_aliceIdent == null) {
-                            // readAliceRouterIdentity already called fail
-                            return;
-                        }
-                        SigType type = _aliceIdent.getSigningPublicKey().getType();
-                        if (type == null) {
-                            fail("Unsupported sig type");
-                            return;
-                        }
-                        changeState(State.IB_GOT_RI);
-                        // handle variable signature size
-                        _sz_aliceIdent_tsA_padding_aliceSigSize = 2 + _aliceIdentSize + 4 + type.getSigLen();
-                        int rem = (_sz_aliceIdent_tsA_padding_aliceSigSize % AES_SIZE);
-                        int padding = 0;
-                        if (rem > 0)
-                            padding = AES_SIZE-rem;
-                        _sz_aliceIdent_tsA_padding_aliceSigSize += padding;
-                        if (_log.shouldLog(Log.DEBUG))
-                            _log.debug(prefix() + "alice ident size decrypted as " + _aliceIdentSize +
-                                       ", making the padding at " + padding + " and total size at " +
-                                       _sz_aliceIdent_tsA_padding_aliceSigSize);
-                    }
-
-                    if (_state == State.IB_GOT_RI &&
-                        _sz_aliceIdent_tsA_padding_aliceSig.size() >= _sz_aliceIdent_tsA_padding_aliceSigSize) {
-                        // we have the remainder of Message #3, i.e. the padding+signature
-                        // Time to verify.
-
-                            if (_log.shouldLog(Log.DEBUG))
-                                _log.debug(prefix() + "got the sig");
-                            verifyInbound(src);
-                            if (_log.shouldLog(Log.DEBUG))
-                                _log.debug(prefix()+"verifying size (sz=" + _sz_aliceIdent_tsA_padding_aliceSig.size()
-                                           + " expected=" + _sz_aliceIdent_tsA_padding_aliceSigSize
-                                           + ' ' + _state
-                                           + ')');
-                            return;
-                    }
-                }
-        }
-
-        // check for remaining data
-        if (STATES_DONE.contains(_state) && src.hasRemaining()) {
-            if (_log.shouldWarn())
-                _log.warn("Received unexpected " + src.remaining() + " on " + this, new Exception());
-        }
-
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug(prefix()+"done with the data, not yet complete or corrupt");
-    }
-
-    /**
-     * We are Bob. We have received enough of message #3 from Alice
-     * to get Alice's RouterIdentity.
-     *
-     * _aliceIdentSize must be set.
-     * _sz_aliceIdent_tsA_padding_aliceSig must contain at least 2 + _aliceIdentSize bytes.
-     *
-     * Sets _aliceIdent so that we
-     * may determine the signature and padding sizes.
-     *
-     * After all of message #3 is received including the signature and
-     * padding, verifyIdentity() must be called.
-     *
-     *  State must be IB_GOT_RI_SIZE.
-     *  Caller must synch.
-     *
-     * @since 0.9.16 pulled out of verifyInbound()
-     */
-    private void readAliceRouterIdentity() {
-        byte b[] = _sz_aliceIdent_tsA_padding_aliceSig.toByteArray();
-
-        try {
-            int sz = _aliceIdentSize;
-            if (sz < MIN_RI_SIZE || sz > MAX_RI_SIZE ||
-                sz > b.length-2) {
-                _context.statManager().addRateData("ntcp.invalidInboundSize", sz);
-                fail("size is invalid", new Exception("size is " + sz));
-                return;
+                //}
             }
-            RouterIdentity alice = new RouterIdentity();
-            ByteArrayInputStream bais = new ByteArrayInputStream(b, 2, sz);
-            alice.readBytes(bais);
-            _aliceIdent = alice;
-        } catch (IOException ioe) {
-            _context.statManager().addRateData("ntcp.invalidInboundIOE", 1);
-            fail("Error verifying peer", ioe);
-        } catch (DataFormatException dfe) {
-            _context.statManager().addRateData("ntcp.invalidInboundDFE", 1);
-            fail("Error verifying peer", dfe);
-        }
-    }
-
-    /**
-     * We are Bob. Verify message #3 from Alice, then send message #4 to Alice.
-     * NTCP 1 only.
-     *
-     * _aliceIdentSize and _aliceIdent must be set.
-     * _sz_aliceIdent_tsA_padding_aliceSig must contain at least
-     *  (2 + _aliceIdentSize + 4 + padding + sig) bytes.
-     *
-     * Sets _aliceIdent so that we
-     *
-     * readAliceRouterIdentity() must have been called previously
-     *
-     * Make sure the signatures are correct, and if they are, update the
-     * NIOConnection with the session key / peer ident / clock skew / iv.
-     * The NIOConnection itself is responsible for registering with the
-     * transport
-     *
-     *  State must be IB_GOT_RI.
-     *  This will always change the state to VERIFIED or CORRUPT.
-     *  Caller must synch.
-     *
-     *  @param buf possibly containing "extra" data for data phase
-     */
-    private void verifyInbound(ByteBuffer buf) {
-        byte b[] = _sz_aliceIdent_tsA_padding_aliceSig.toByteArray();
-        try {
-            int sz = _aliceIdentSize;
-            // her timestamp from message #3
-            long tsA = DataHelper.fromLong(b, 2+sz, 4);
-            // _tsB is when we sent message #2
-            // Adjust backward by RTT/2
-            long now = _context.clock().now();
-            // rtt from sending #2 to receiving #3
-            long rtt = now - _con.getCreated();
-            _peerSkew = (now - (tsA * 1000) - (rtt / 2) + 500) / 1000; 
-
-            ByteArrayStream baos = new ByteArrayStream(256 + 256 + 32 + 4 + 4);
-            baos.write(_X);
-            baos.write(_Y);
-            baos.write(_context.routerHash().getData());
-            baos.write(DataHelper.toLong(4, tsA));
-            baos.write(DataHelper.toLong(4, _tsB));
-            //baos.write(b, 2+sz+4, b.length-2-sz-4-Signature.SIGNATURE_BYTES);
-
-            byte toVerify[] = baos.toByteArray();
-
-            // handle variable signature size
-            SigType type = _aliceIdent.getSigningPublicKey().getType();
-            if (type == null) {
-                fail("unsupported sig type");
-                return;
-            }
-            byte s[] = new byte[type.getSigLen()];
-            System.arraycopy(b, b.length-s.length, s, 0, s.length);
-            Signature sig = new Signature(type, s);
-            boolean ok = _context.dsa().verifySignature(sig, toVerify, _aliceIdent.getSigningPublicKey());
-            Hash aliceHash = _aliceIdent.calculateHash();
-            if (ok) {
-                ok = verifyInbound(aliceHash);
-            }
-            if (ok) {
-                _con.setRemotePeer(_aliceIdent);
-                sendInboundConfirm(aliceHash, tsA);
-                if (_log.shouldLog(Log.DEBUG))
-                    _log.debug(prefix()+"e_bobSig is " + _e_bobSig.length + " bytes long");
-                byte iv[] = _curEncrypted;  // reuse buf
-                System.arraycopy(_e_bobSig, _e_bobSig.length-AES_SIZE, iv, 0, AES_SIZE);
-                // this does not copy the IV, do not release to cache
-                // We are Bob, she is Alice, clock skew is Alice-Bob
-                // skew in seconds
-                _con.finishInboundEstablishment(_dh.getSessionKey(), _peerSkew, iv, _prevEncrypted);
-                changeState(State.VERIFIED);
-                if (buf.hasRemaining()) {
-                    // process "extra" data
-                    // This is unlikely for inbound, as we must reply with message 4
-                    if (_log.shouldWarn())
-                        _log.warn("extra data " + buf.remaining() + " on " + this);
-                     _con.recvEncryptedI2NP(buf);
-                }
-                releaseBufs(true);
-                if (_log.shouldLog(Log.INFO))
-                    _log.info(prefix()+"Verified remote peer as " + aliceHash);
-            } else {
-                _context.statManager().addRateData("ntcp.invalidInboundSignature", 1);
-                // verifyInbound(aliceHash) called fail()
-            }
-        } catch (IOException ioe) {
-            _context.statManager().addRateData("ntcp.invalidInboundIOE", 1);
-            fail("Error verifying peer", ioe);
         }
     }
 
@@ -607,45 +272,6 @@ class InboundEstablishState extends EstablishBase implements NTCP2Payload.Payloa
         return rv;
     }
 
-    /**
-     *  We are Bob. Send message #4 to Alice.
-     *
-     *  State must be VERIFIED.
-     *  Caller must synch.
-     *
-     *  @param h Alice's Hash
-     */
-    private void sendInboundConfirm(Hash h, long tsA) {
-        // send Alice E(S(X+Y+Alice.identHash+tsA+tsB), sk, prev)
-        byte toSign[] = new byte[XY_SIZE + XY_SIZE + 32+4+4];
-        int off = 0;
-        System.arraycopy(_X, 0, toSign, off, XY_SIZE); off += XY_SIZE;
-        System.arraycopy(_Y, 0, toSign, off, XY_SIZE); off += XY_SIZE;
-        System.arraycopy(h.getData(), 0, toSign, off, 32); off += 32;
-        DataHelper.toLong(toSign, off, 4, tsA); off += 4;
-        DataHelper.toLong(toSign, off, 4, _tsB); off += 4;
-
-        // handle variable signature size
-        Signature sig = _context.dsa().sign(toSign, _context.keyManager().getSigningPrivateKey());
-        int siglen = sig.length();
-        int rem = siglen % AES_SIZE;
-        int padding;
-        if (rem > 0)
-            padding = AES_SIZE - rem;
-        else
-            padding = 0;
-        byte preSig[] = new byte[siglen + padding];
-        System.arraycopy(sig.getData(), 0, preSig, 0, siglen);
-        if (padding > 0)
-            _context.random().nextBytes(preSig, siglen, padding);
-        _e_bobSig = new byte[preSig.length];
-        _context.aes().encrypt(preSig, 0, _e_bobSig, 0, _dh.getSessionKey(), _e_hXY_tsB, HXY_TSB_PAD_SIZE - AES_SIZE, _e_bobSig.length);
-
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug(prefix() + "Sending encrypted inbound confirmation");
-        _transport.getPumper().wantsWrite(_con, _e_bobSig);
-    }
-
     //// NTCP2 below here
 
     /**
diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java
index 0c4d4a80c0738fc0e0e1b9621a7d323799182bcf..79867850c552e413a926856fbd2c8847440e4d39 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java
@@ -58,26 +58,6 @@ import net.i2p.util.VersionComparator;
  *
  * Public only for UI peers page. Not a public API, not for external use.
  *
- * The NTCP transport sends individual I2NP messages AES/256/CBC encrypted with
- * a simple checksum.  The unencrypted message is encoded as follows:
- *<pre>
- *  +-------+-------+--//--+---//----+-------+-------+-------+-------+
- *  | sizeof(data)  | data | padding | adler checksum of sz+data+pad |
- *  +-------+-------+--//--+---//----+-------+-------+-------+-------+
- *</pre>
- * That message is then encrypted with the DH/2048 negotiated session key
- * (station to station authenticated per the EstablishState class) using the
- * last 16 bytes of the previous encrypted message as the IV.
- *
- * One special case is a metadata message where the sizeof(data) is 0.  In
- * that case, the unencrypted message is encoded as:
- *<pre>
- *  +-------+-------+-------+-------+-------+-------+-------+-------+
- *  |       0       |      timestamp in seconds     | uninterpreted             
- *  +-------+-------+-------+-------+-------+-------+-------+-------+
- *          uninterpreted           | adler checksum of sz+data+pad |
- *  +-------+-------+-------+-------+-------+-------+-------+-------+
- *</pre>
  *
  */
 public class NTCPConnection implements Closeable {
@@ -249,8 +229,9 @@ public class NTCPConnection implements Closeable {
         this(ctx, transport, remAddr, false);
         _remotePeer = remotePeer;
         _version = version;
-        if (version == 1) {
-            _establishState = new OutboundEstablishState(ctx, transport, this);
+        if (version != 2) {
+            throw new IllegalArgumentException("bad version " + version);
+            //_establishState = new OutboundEstablishState(ctx, transport, this);
         } else {
             try {
                 _establishState = new OutboundNTCP2State(ctx, transport, this);
@@ -329,58 +310,6 @@ public class NTCPConnection implements Closeable {
      */
     public void setRemotePeer(RouterIdentity ident) { _remotePeer = ident; }
 
-    /** 
-     * We are Bob. NTCP1 only.
-     *
-     * Caller MUST call recvEncryptedI2NP() after, for any remaining bytes in receive buffer
-     *
-     * @param clockSkew OUR clock minus ALICE's clock in seconds (may be negative, obviously, but |val| should
-     *                  be under 1 minute)
-     * @param prevWriteEnd exactly 16 bytes, not copied, do not corrupt, the write AES IV
-     * @param prevReadEnd 16 or more bytes, last 16 bytes copied as the read AES IV
-     */
-    void finishInboundEstablishment(SessionKey key, long clockSkew, byte prevWriteEnd[], byte prevReadEnd[]) {
-        NTCPConnection toClose = locked_finishInboundEstablishment(key, clockSkew, prevWriteEnd, prevReadEnd);
-        if (toClose != null) {
-            int drained = toClose.drainOutboundTo(_outbound);
-            if (_log.shouldWarn())
-                _log.warn("Old connection closed: " + toClose + " replaced by " + this + "; drained " + drained);
-            _context.statManager().addRateData("ntcp.inboundEstablishedDuplicate", toClose.getUptime());
-            toClose.close();
-        }
-        enqueueInfoMessage();
-    }
-    
-    /** 
-     * We are Bob. NTCP1 only.
-     *
-     * @param clockSkew OUR clock minus ALICE's clock in seconds (may be negative, obviously, but |val| should
-     *                  be under 1 minute)
-     * @param prevWriteEnd exactly 16 bytes, not copied, do not corrupt, the write AES IV
-     * @param prevReadEnd 16 or more bytes, last 16 bytes copied as the read AES IV
-     * @return old conn to be closed by caller, or null
-     */
-    private synchronized NTCPConnection locked_finishInboundEstablishment(
-            SessionKey key, long clockSkew, byte prevWriteEnd[], byte prevReadEnd[]) {
-        if (_establishState == EstablishBase.VERIFIED) {
-            IllegalStateException ise = new IllegalStateException("Already finished on " + this);
-            _log.error("Already finished", ise);
-            throw ise;
-        }
-        byte[] prevReadBlock = new byte[BLOCK_SIZE];
-        System.arraycopy(prevReadEnd, prevReadEnd.length - BLOCK_SIZE, prevReadBlock, 0, BLOCK_SIZE);
-        _curReadState = new NTCP1ReadState(prevReadBlock);
-        _sessionKey = key;
-        _clockSkew = clockSkew;
-        _prevWriteEnd = prevWriteEnd;
-        _establishedOn = _context.clock().now();
-        NTCPConnection rv = _transport.inboundEstablished(this);
-        _nextMetaTime = _establishedOn + (META_FREQUENCY / 2) + _context.random().nextInt(META_FREQUENCY);
-        _nextInfoTime = _establishedOn + (INFO_FREQUENCY / 2) + _context.random().nextInt(INFO_FREQUENCY);
-        _establishState = EstablishBase.VERIFIED;
-        return rv;
-    }
-
     /**
      * A positive number means our clock is ahead of theirs.
      *  @return seconds
@@ -658,10 +587,7 @@ public class NTCPConnection implements Closeable {
      *  con is established, so we know what the version is.
      */
     void enqueueInfoMessage() {
-        if (_version == 1) {
-            enqueueInfoMessageNTCP1();
-            // may change to 2 for inbound
-        } else if (_isInbound) {
+        if (_isInbound) {
             // TODO or if outbound and it's not right at the beginning
             // TODO flood
             sendOurRouterInfo(false);
@@ -669,56 +595,6 @@ public class NTCPConnection implements Closeable {
         // don't need to send for NTCP 2 outbound, it's in msg 3
     }
     
-    /**
-     *  Inject a DatabaseStoreMessage with our RouterInfo. NTCP 1 only.
-     */
-    private void enqueueInfoMessageNTCP1() {
-        int priority = INFO_PRIORITY;
-        if (_log.shouldDebug())
-            _log.debug("SENDING INFO message pri. " + priority + ": " + toString());
-        DatabaseStoreMessage dsm = new DatabaseStoreMessage(_context);
-        dsm.setEntry(_context.router().getRouterInfo());
-        // We are injecting directly, so we can use a null target.
-        OutNetMessage infoMsg = new OutNetMessage(_context, dsm, _context.clock().now()+10*1000, priority, null);
-        infoMsg.beginSend();
-        send(infoMsg);
-    }
-
-    /** 
-     * We are Alice. NTCP1 only.
-     *
-     * Caller MUST call recvEncryptedI2NP() after, for any remaining bytes in receive buffer
-     *
-     * @param clockSkew OUR clock minus BOB's clock in seconds (may be negative, obviously, but |val| should
-     *                  be under 1 minute)
-     * @param prevWriteEnd exactly 16 bytes, not copied, do not corrupt
-     * @param prevReadEnd 16 or more bytes, last 16 bytes copied
-     */
-    synchronized void finishOutboundEstablishment(SessionKey key, long clockSkew, byte prevWriteEnd[], byte prevReadEnd[]) {
-        if (_establishState == EstablishBase.VERIFIED) {
-            IllegalStateException ise = new IllegalStateException("Already finished on " + this);
-            _log.error("Already finished", ise);
-            throw ise;
-        }
-        byte[] prevReadBlock = new byte[BLOCK_SIZE];
-        System.arraycopy(prevReadEnd, prevReadEnd.length - BLOCK_SIZE, prevReadBlock, 0, BLOCK_SIZE);
-        _curReadState = new NTCP1ReadState(prevReadBlock);
-        _sessionKey = key;
-        _clockSkew = clockSkew;
-        _prevWriteEnd = prevWriteEnd;
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("outbound established (key=" + key + " skew=" + clockSkew +
-                       " prevWriteEnd: " + Base64.encode(prevWriteEnd) + " prevReadBlock: " + Base64.encode(prevReadBlock));
-
-        _establishedOn = _context.clock().now();
-        _establishState = EstablishBase.VERIFIED;
-        _transport.markReachable(getRemotePeer().calculateHash(), false);
-        _nextMetaTime = _establishedOn + (META_FREQUENCY / 2) + _context.random().nextInt(META_FREQUENCY);
-        _nextInfoTime = _establishedOn + (INFO_FREQUENCY / 2) + _context.random().nextInt(INFO_FREQUENCY);
-        if (!_outbound.isEmpty())
-            _transport.getWriter().wantsWrite(this, "outbound established");
-    }
-    
     /**
      * Prepare the next I2NP message for transmission.  This should be run from
      * the Writer thread pool. NTCP 1 or 2.
@@ -738,98 +614,7 @@ public class NTCPConnection implements Closeable {
         if (!isEstablished()) {
             return;
         }
-        if (_version == 1)
-            prepareNextWriteFast(prep);
-        else
-            prepareNextWriteNTCP2(prep);
-    }
-
-    /**
-     * Prepare the next I2NP message for transmission.  This should be run from
-     * the Writer thread pool. NTCP 1 only.
-     *
-     * Caller must synchronize.
-     * @param buf a PrepBuffer to use as scratch space
-     *
-     */
-    private void prepareNextWriteFast(PrepBuffer buf) {
-        long now = _context.clock().now();
-        if (_nextMetaTime <= now) {
-            sendMeta();
-            _nextMetaTime = now + (META_FREQUENCY / 2) + _context.random().nextInt(META_FREQUENCY / 2);
-        }
-      
-        OutNetMessage msg;
-        synchronized (_currentOutbound) {
-            if (!_currentOutbound.isEmpty()) {
-                if (_log.shouldLog(Log.INFO))
-                    _log.info("attempt for multiple outbound messages with " + _currentOutbound.size() + " already waiting and " + _outbound.size() + " queued");
-                return;
-            }
-            while (true) {
-                msg = _outbound.poll();
-                if (msg == null)
-                    return;
-                if (msg.getExpiration() >= now)
-                    break;
-                if (_log.shouldWarn())
-                    _log.warn("dropping message expired on queue: " + msg + " on " + this);
-                _transport.afterSend(msg, false, false, msg.getLifetime());
-            }
-            _currentOutbound.add(msg);
-        }
-
-        bufferedPrepare(msg, buf);
-        _context.aes().encrypt(buf.unencrypted, 0, buf.encrypted, 0, _sessionKey, _prevWriteEnd, 0, buf.unencryptedLength);
-        System.arraycopy(buf.encrypted, buf.encrypted.length-16, _prevWriteEnd, 0, _prevWriteEnd.length);
-        _transport.getPumper().wantsWrite(this, buf.encrypted);
-
-        // for every 6-12 hours that we are connected to a peer, send them
-	// our updated netDb info (they may not accept it and instead query
-	// the floodfill netDb servers, but they may...)
-        if (_nextInfoTime <= now) {
-            // perhaps this should check to see if we are bw throttled, etc?
-            enqueueInfoMessage();
-            _nextInfoTime = now + (INFO_FREQUENCY / 2) + _context.random().nextInt(INFO_FREQUENCY);
-        }
-    }
-    
-    /**
-     * Serialize the message/checksum/padding/etc for transmission, but leave off
-     * the encryption.  This should be called from a Writer thread
-     * 
-     * @param msg message to send
-     * @param buf PrepBuffer to use as scratch space
-     */
-    private void bufferedPrepare(OutNetMessage msg, PrepBuffer buf) {
-        I2NPMessage m = msg.getMessage();
-        // 2 offset for size
-        int sz = m.toByteArray(buf.unencrypted, 2) - 2;
-        int min = 2 + sz + 4;
-        int rem = min % 16;
-        int padding = 0;
-        if (rem > 0)
-            padding = 16 - rem;
-        buf.unencryptedLength = min+padding;
-        DataHelper.toLong(buf.unencrypted, 0, 2, sz);
-        if (padding > 0) {
-            _context.random().nextBytes(buf.unencrypted, 2+sz, padding);
-        }
-        
-        buf.crc.update(buf.unencrypted, 0, buf.unencryptedLength-4);
-        
-        long val = buf.crc.getValue();
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Outbound message " + _messagesWritten + " has crc " + val
-                       + " sz=" +sz + " rem=" + rem + " padding=" + padding);
-        
-        DataHelper.toLong(buf.unencrypted, buf.unencryptedLength-4, 4, val);
-        // TODO object churn
-        // 1) store the length only
-        // 2) in prepareNextWriteFast(), pull a byte buffer off a queue and encrypt to that
-        // 3) change EventPumper.wantsWrite() to take a ByteBuffer arg
-        // 4) in EventPumper.processWrite(), release the byte buffer
-        buf.encrypted = new byte[buf.unencryptedLength];
+        prepareNextWriteNTCP2(prep);
     }
 
     static class PrepBuffer {
@@ -1386,39 +1171,6 @@ public class NTCPConnection implements Closeable {
         _curReadState.receive(buf);
     }
     
-   /* 
-    * One special case is a metadata message where the sizeof(data) is 0.  In
-    * that case, the unencrypted message is encoded as:
-    * 
-    * <pre>
-    *  +-------+-------+-------+-------+-------+-------+-------+-------+
-    *  |       0       |      timestamp in seconds     | uninterpreted             
-    *  +-------+-------+-------+-------+-------+-------+-------+-------+
-    *          uninterpreted           | adler checksum of sz+data+pad |
-    *  +-------+-------+-------+-------+-------+-------+-------+-------+
-    * </pre>
-    * 
-    *  Caller must synch
-    * 
-    *  @param unencrypted 16 bytes starting at off
-    *  @param off the offset
-    */
-    private void readMeta(byte unencrypted[], int off) {
-        Adler32 crc = new Adler32();
-        crc.update(unencrypted, off, META_SIZE - 4);
-        long expected = crc.getValue();
-        long read = DataHelper.fromLong(unencrypted, off + META_SIZE - 4, 4);
-        if (read != expected) {
-            if (_log.shouldLog(Log.WARN))
-                _log.warn("I2NP metadata message had a bad CRC value");
-            _context.statManager().addRateData("ntcp.corruptMetaCRC", 1);
-            close();
-            return;
-        }
-        long ts = DataHelper.fromLong(unencrypted, off + 2, 4);
-        receiveTimestamp(ts);
-    }
-
     /**
      *  Handle a received timestamp, NTCP 1 or 2.
      *  Caller must synch
@@ -1443,37 +1195,6 @@ public class NTCPConnection implements Closeable {
         _clockSkew = newSkew;
     }
 
-    /**
-     * One special case is a metadata message where the sizeof(data) is 0.  In
-     * that case, the unencrypted message is encoded as:
-     *
-     *<pre>
-     *  +-------+-------+-------+-------+-------+-------+-------+-------+
-     *  |       0       |      timestamp in seconds     | uninterpreted             
-     *  +-------+-------+-------+-------+-------+-------+-------+-------+
-     *          uninterpreted           | adler checksum of sz+data+pad |
-     *  +-------+-------+-------+-------+-------+-------+-------+-------+
-     *</pre>
-     *
-     * Caller must synchronize.
-     */
-    private void sendMeta() {
-        byte[] data = new byte[META_SIZE];
-        DataHelper.toLong(data, 0, 2, 0);
-        DataHelper.toLong(data, 2, 4, (_context.clock().now() + 500) / 1000);
-        _context.random().nextBytes(data, 6, 6);
-        Adler32 crc = new Adler32();
-        crc.update(data, 0, META_SIZE - 4);
-        DataHelper.toLong(data, META_SIZE - 4, 4, crc.getValue());
-        _context.aes().encrypt(data, 0, data, 0, _sessionKey, _prevWriteEnd, 0, META_SIZE);
-        System.arraycopy(data, META_SIZE - 16, _prevWriteEnd, 0, _prevWriteEnd.length);
-        // perhaps this should skip the bw limiter to reduce clock skew issues?
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Sending NTCP metadata");
-        _sendingMeta = true;
-        _transport.getPumper().wantsWrite(this, data);
-    }
-    
     private static final int MAX_HANDLERS = 4;
 
     /**
@@ -1514,319 +1235,6 @@ public class NTCPConnection implements Closeable {
         public int getFramesReceived();
     }
 
-    /**
-     * Read the unencrypted message (16 bytes at a time).
-     * verify the checksum, and pass it on to
-     * an I2NPMessageHandler.  The unencrypted message is encoded as follows:
-     *
-     *<pre>
-     *  +-------+-------+--//--+---//----+-------+-------+-------+-------+
-     *  | sizeof(data)  | data | padding | adler checksum of sz+data+pad |
-     *  +-------+-------+--//--+---//----+-------+-------+-------+-------+
-     *</pre>
-     *
-     * sizeof(data)+data+pad+crc.
-     *
-     * perhaps to reduce the per-con memory footprint, we can acquire/release
-     * the ReadState._data and ._bais when _size is > 0, so there are only
-     * J 16KB buffers for the cons actually transmitting, instead of one per
-     * con (including idle ones)
-     *
-     * Call all methods from synchronized parent method.
-     *
-     */
-    private class NTCP1ReadState implements ReadState {
-        private int _size;
-        private ByteArray _dataBuf;
-        private int _nextWrite;
-        private final Adler32 _crc;
-        private long _stateBegin;
-        private int _blocks;
-        /** encrypted block of the current I2NP message being read */
-        private byte _curReadBlock[];
-        /** next byte to which data should be placed in the _curReadBlock */
-        private int _curReadBlockIndex;
-        private final byte _decryptBlockBuf[];
-        /** last AES block of the encrypted I2NP message (to serve as the next block's IV) */
-        private byte _prevReadBlock[];
-
-        /**
-         *  @param prevReadBlock 16 bytes AES IV
-         */
-        public NTCP1ReadState(byte[] prevReadBlock) {
-            _crc = new Adler32();
-            _prevReadBlock = prevReadBlock;
-            _curReadBlock = new byte[BLOCK_SIZE];
-            _decryptBlockBuf = new byte[BLOCK_SIZE];
-            init();
-        }
-
-        private void init() {
-            _size = -1;
-            _nextWrite = 0;
-            _stateBegin = -1;
-            _blocks = -1;
-            _crc.reset();
-            if (_dataBuf != null)
-                releaseReadBuf(_dataBuf);
-            _dataBuf = null;
-            _curReadBlockIndex = 0;
-        }
-
-        /** @since 0.9.36 */
-        public void destroy() {
-            if (_dataBuf != null) {
-                releaseReadBuf(_dataBuf);
-                _dataBuf = null;
-            }
-            // TODO zero things out
-        }
-        
-        /**
-         * Connection must be established!
-         *
-         * The contents of the buffer include some fraction of one or more
-         * encrypted and encoded I2NP messages.  individual i2np messages are
-         * encoded as "sizeof(data)+data+pad+crc", and those are encrypted
-         * with the session key and the last 16 bytes of the previous encrypted
-         * i2np message.
-         *
-         * The NTCP connection now owns the buffer
-         * BUT it must copy out the data
-         * as reader will call EventPumper.releaseBuf().
-         *
-         * @since 0.9.36 moved from parent class
-         */
-        public void receive(ByteBuffer buf) {
-            // hasArray() is false for direct buffers, at least on my system...
-            if (_curReadBlockIndex == 0 && buf.hasArray()) {
-                // fast way
-                int tot = buf.remaining();
-                if (tot >= 32 && tot % 16 == 0) {
-                    recvEncryptedFast(buf);
-                    return;
-                }
-            }
-
-            while (buf.hasRemaining() && !_closed.get()) {
-                int want = Math.min(buf.remaining(), BLOCK_SIZE - _curReadBlockIndex);
-                if (want > 0) {
-                    buf.get(_curReadBlock, _curReadBlockIndex, want);
-                    _curReadBlockIndex += want;
-                }
-                if (_curReadBlockIndex >= BLOCK_SIZE) {
-                    // cbc
-                    _context.aes().decryptBlock(_curReadBlock, 0, _sessionKey, _decryptBlockBuf, 0);
-                    xor16(_prevReadBlock, _decryptBlockBuf);
-                    boolean ok = recvUnencryptedI2NP();
-                    if (!ok) {
-                        if (_log.shouldLog(Log.INFO))
-                            _log.info("Read buffer " + System.identityHashCode(buf) + " contained corrupt data, IV was: " + Base64.encode(_decryptBlockBuf));
-                        _context.statManager().addRateData("ntcp.corruptDecryptedI2NP", 1);
-                        return;
-                    }
-                    byte swap[] = _prevReadBlock;
-                    _prevReadBlock = _curReadBlock;
-                    _curReadBlock = swap;
-                    _curReadBlockIndex = 0;
-                }
-            }
-        }
-
-        /**
-         *  Decrypt directly out of the ByteBuffer instead of copying the bytes
-         *  16 at a time to the _curReadBlock / _prevReadBlock flip buffers.
-         *
-         *  More efficient but can only be used if buf.hasArray == true AND
-         *  _curReadBlockIndex must be 0 and buf.getRemaining() % 16 must be 0
-         *  and buf.getRemaining() must be >= 16.
-         *  All this is true for most incoming buffers.
-         *  In theory this could be fixed up to handle the other cases too but that's hard.
-         *  Caller must synchronize!
-         *
-         *  @since 0.8.12, moved from parent class in 0.9.36
-         */
-        private void recvEncryptedFast(ByteBuffer buf) {
-            byte[] array = buf.array();
-            int pos = buf.arrayOffset() + buf.position();
-            int end = pos + buf.remaining();
-
-            // Copy to _curReadBlock for next IV...
-            System.arraycopy(array, end - BLOCK_SIZE, _curReadBlock, 0, BLOCK_SIZE);
-            // call aes().decrypt() to decrypt all at once, in place
-            // decrypt() will offload to the JVM/OS for larger sizes
-            _context.aes().decrypt(array, pos, array, pos, _sessionKey, _prevReadBlock, buf.remaining());
-
-            for ( ; pos < end; pos += BLOCK_SIZE) {
-                boolean ok = receiveBlock(array, pos);
-                if (!ok) {
-                    if (_log.shouldLog(Log.INFO))
-                        _log.info("Read buffer " + System.identityHashCode(buf) + " contained corrupt data");
-                    _context.statManager().addRateData("ntcp.corruptDecryptedI2NP", 1);
-                    return;
-                }
-            }
-            // ...and flip to _prevReadBlock for next time
-            byte swap[] = _prevReadBlock;
-            _prevReadBlock = _curReadBlock;
-            _curReadBlock = swap;
-        }
-    
-        /**
-         *  Append the next 16 bytes of cleartext to the read state.
-         *  _decryptBlockBuf contains another cleartext block of I2NP to parse.
-         *  Caller must synchronize!
-         *
-         *  @return success
-         *  @since 0.9.36 moved from parent class
-         */
-        private boolean recvUnencryptedI2NP() {
-            return receiveBlock(_decryptBlockBuf, 0);
-        }
-
-        /**
-         *  Caller must synchronize
-         *  @param buf 16 bytes starting at off
-         *  @param off offset
-         *  @return success, only false on initial block with invalid size
-         */
-        private boolean receiveBlock(byte buf[], int off) {
-            if (_size == -1) {
-                return receiveInitial(buf, off);
-            } else {
-                receiveSubsequent(buf, off);
-                return true;
-            }
-        }
-
-        /**
-         *  Caller must synchronize
-         *
-         *  @param buf 16 bytes starting at off
-         *  @param off offset
-         *  @return success
-         */
-        private boolean receiveInitial(byte buf[], int off) {
-            _size = (int)DataHelper.fromLong(buf, off, 2);
-            if (_size > BUFFER_SIZE) {
-                // this is typically an AES decryption error, not actually a large I2NP message
-                if (_log.shouldLog(Log.WARN))
-                    _log.warn("I2NP message too big - size: " + _size + " Closing " + NTCPConnection.this.toString(), new Exception());
-                _context.statManager().addRateData("ntcp.corruptTooLargeI2NP", _size);
-                close();
-                return false;
-            }
-            if (_size == 0) {
-                readMeta(buf, off);
-                init();
-            } else {
-                _stateBegin = _context.clock().now();
-                _dataBuf = acquireReadBuf();
-                System.arraycopy(buf, off + 2, _dataBuf.getData(), 0, BLOCK_SIZE - 2);
-                _nextWrite += BLOCK_SIZE - 2;
-                _crc.update(buf, off, BLOCK_SIZE);
-                _blocks++;
-                if (_log.shouldLog(Log.DEBUG))
-                    _log.debug("new I2NP message with size: " + _size + " for message " + _messagesRead);
-            }
-            return true;
-        }
-
-        /**
-         *  Caller must synchronize
-         *
-         *  @param buf 16 bytes starting at off
-         *  @param off offset
-         */
-        private void receiveSubsequent(byte buf[], int off) {
-            _blocks++;
-            int remaining = _size - _nextWrite;
-            int blockUsed = Math.min(BLOCK_SIZE, remaining);
-            if (remaining > 0) {
-                System.arraycopy(buf, off, _dataBuf.getData(), _nextWrite, blockUsed);
-                _nextWrite += blockUsed;
-                remaining -= blockUsed;
-            }
-            if ( (remaining <= 0) && (BLOCK_SIZE - blockUsed < 4) ) {
-                // we've received all the data but not the 4-byte checksum
-                if (_log.shouldLog(Log.DEBUG))
-                    _log.debug("crc wraparound required on block " + _blocks + " in message " + _messagesRead);
-                _crc.update(buf, off, BLOCK_SIZE);
-                return;
-            } else if (remaining <= 0) {
-                receiveLastBlock(buf, off);
-            } else {
-                _crc.update(buf, off, BLOCK_SIZE);
-            }
-        }
-
-        /**
-         *  This checks the checksum in buf only.
-         *  All previous data, including that in buf, must have been copied to _dataBuf.
-         *  Note that the checksum does not cover the padding.
-         *  Caller must synchronize.
-         *
-         *  @param buf 16 bytes starting at off
-         *  @param off offset of the 16-byte block (NOT of the checksum only)
-         */
-        private void receiveLastBlock(byte buf[], int off) {
-            // on the last block
-            long expectedCrc = DataHelper.fromLong(buf, off + BLOCK_SIZE - 4, 4);
-            _crc.update(buf, off, BLOCK_SIZE - 4);
-            long val = _crc.getValue();
-            if (val == expectedCrc) {
-                try {
-                    I2NPMessageHandler h = acquireHandler(_context);
-
-                    // Don't do readMessage(InputStream). I2NPMessageImpl.readBytes() copies the data
-                    // from a stream to a temp buffer.
-                    // We could extend BAIS to adjust the protected count variable to _size
-                    // so that readBytes() doesn't read too far, but it could still read too far.
-                    // So use the new handler method that limits the size.
-                    h.readMessage(_dataBuf.getData(), 0, _size);
-                    I2NPMessage read = h.lastRead();
-                    long timeToRecv = _context.clock().now() - _stateBegin;
-                    releaseHandler(h);
-                    if (_log.shouldLog(Log.DEBUG))
-                        _log.debug("I2NP message " + _messagesRead + "/" + (read != null ? read.getUniqueId() : 0) 
-                                   + " received after " + timeToRecv + " with " + _size +"/"+ (_blocks*16) + " bytes on " + NTCPConnection.this.toString());
-                    _context.statManager().addRateData("ntcp.receiveTime", timeToRecv);
-                    _context.statManager().addRateData("ntcp.receiveSize", _size);
-
-                    // FIXME move end of try block here.
-                    // On the input side, move releaseHandler() and init() to a finally block.
-
-                    if (read != null) {
-                        _transport.messageReceived(read, _remotePeer, null, timeToRecv, _size);
-                        _lastReceiveTime = _context.clock().now();
-                        _messagesRead.incrementAndGet();
-                    }
-                } catch (I2NPMessageException ime) {
-                    if (_log.shouldLog(Log.WARN)) {
-                        _log.warn("Error parsing I2NP message on " + NTCPConnection.this +
-                                  "\nDUMP:\n" + HexDump.dump(_dataBuf.getData(), 0, _size),
-                                  ime);
-                    }
-                    _context.statManager().addRateData("ntcp.corruptI2NPIME", 1);
-                }
-            } else {
-                if (_log.shouldLog(Log.WARN))
-                    _log.warn("CRC incorrect for message " + _messagesRead + " (calc=" + val + " expected=" + expectedCrc +
-                              ") size=" + _size + " blocks=" + _blocks + " on: " + NTCPConnection.this);
-                    _context.statManager().addRateData("ntcp.corruptI2NPCRC", 1);
-            }
-            // get it ready for the next I2NP message
-            init();
-        }
-
-        /*
-         * Dummy.
-         * @return 0 always
-         * @since 0.9.36
-         */
-        public int getFramesReceived() { return 0; }
-    }
-
     //// NTCP2 below here
 
     /** 
diff --git a/router/java/src/net/i2p/router/transport/ntcp/OutboundEstablishState.java b/router/java/src/net/i2p/router/transport/ntcp/OutboundEstablishState.java
deleted file mode 100644
index c40573803b1bbf2415563a261b223da46b34c795..0000000000000000000000000000000000000000
--- a/router/java/src/net/i2p/router/transport/ntcp/OutboundEstablishState.java
+++ /dev/null
@@ -1,317 +0,0 @@
-package net.i2p.router.transport.ntcp;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-
-import net.i2p.crypto.SigType;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Hash;
-import net.i2p.data.router.RouterIdentity;
-import net.i2p.data.Signature;
-import net.i2p.router.Router;
-import net.i2p.router.RouterContext;
-import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
-import net.i2p.util.Log;
-import net.i2p.util.SimpleByteCache;
-
-/**
- *
- *  NTCP 1 only. We are Alice.
- *
- *  @since 0.9.35 pulled out of EstablishState
- */
-class OutboundEstablishState extends EstablishBase {
-    
-    public OutboundEstablishState(RouterContext ctx, NTCPTransport transport, NTCPConnection con) {
-        super(ctx, transport, con);
-        _state = State.OB_INIT;
-        ctx.sha().calculateHash(_X, 0, XY_SIZE, _hX_xor_bobIdentHash, 0);
-        xor32(con.getRemotePeer().calculateHash().getData(), _hX_xor_bobIdentHash);
-        // _prevEncrypted will be created later
-    }
-
-    /**
-     * Parse the contents of the buffer as part of the handshake.
-     *
-     * All data must be copied out of the buffer as Reader.processRead()
-     * will return it to the pool.
-     *
-     * If there are additional data in the buffer after the handshake is complete,
-     * the EstablishState is responsible for passing it to NTCPConnection.
-     */
-    @Override
-    public synchronized void receive(ByteBuffer src) {
-        super.receive(src);
-        if (!src.hasRemaining())
-            return; // nothing to receive
-        receiveOutbound(src);
-    }
-
-    /**
-     *  Get the NTCP version
-     *  @return 1
-     *  @since 0.9.35
-     */
-    public int getVersion() { return 1; }
-
-    /**
-     *  We are Alice, so receive these bytes as part of an outbound connection.
-     *  This method receives messages 2 and 4, and sends message 3.
-     *
-     *  All data must be copied out of the buffer as Reader.processRead()
-     *  will return it to the pool.
-     *
-     *  Caller must synch.
-     *
-     */
-    private void receiveOutbound(ByteBuffer src) {
-
-        // recv Y+E(H(X+Y)+tsB, sk, Y[239:255])
-        // Read in Y, which is the first part of message #2
-        if (_state == State.OB_SENT_X && src.hasRemaining()) {
-            int toGet = Math.min(src.remaining(), XY_SIZE - _received);
-            src.get(_Y, _received, toGet);
-            _received += toGet;
-            if (_received < XY_SIZE)
-                return;
-
-            try {
-                _dh.setPeerPublicValue(_Y);
-                _dh.getSessionKey(); // force the calc
-                if (_log.shouldLog(Log.DEBUG))
-                    _log.debug(prefix()+"DH session key calculated (" + _dh.getSessionKey().toBase64() + ")");
-                changeState(State.OB_GOT_Y);
-                _received = 0;
-            } catch (DHSessionKeyBuilder.InvalidPublicParameterException e) {
-                _context.statManager().addRateData("ntcp.invalidDH", 1);
-                fail("Invalid X", e);
-                return;
-            } catch (IllegalStateException ise) {
-                // setPeerPublicValue()
-                fail("reused keys?", ise);
-                return;
-            }
-        }
-
-        // Read in the rest of message #2
-        if (_state == State.OB_GOT_Y && src.hasRemaining()) {
-            int toGet = Math.min(src.remaining(), HXY_TSB_PAD_SIZE - _received);
-            src.get(_e_hXY_tsB, _received, toGet);
-            _received += toGet;
-            if (_received < HXY_TSB_PAD_SIZE)
-                return;
-
-            if (_log.shouldLog(Log.DEBUG)) _log.debug(prefix() + "received _e_hXY_tsB fully");
-            byte hXY_tsB[] = new byte[HXY_TSB_PAD_SIZE];
-            _context.aes().decrypt(_e_hXY_tsB, 0, hXY_tsB, 0, _dh.getSessionKey(), _Y, XY_SIZE-AES_SIZE, HXY_TSB_PAD_SIZE);
-            byte XY[] = new byte[XY_SIZE + XY_SIZE];
-            System.arraycopy(_X, 0, XY, 0, XY_SIZE);
-            System.arraycopy(_Y, 0, XY, XY_SIZE, XY_SIZE);
-            byte[] h = SimpleByteCache.acquire(HXY_SIZE);
-            _context.sha().calculateHash(XY, 0, XY_SIZE + XY_SIZE, h, 0);
-            if (!DataHelper.eq(h, 0, hXY_tsB, 0, HXY_SIZE)) {
-                SimpleByteCache.release(h);
-                _context.statManager().addRateData("ntcp.invalidHXY", 1);
-                fail("Invalid H(X+Y) - mitm attack attempted?");
-                return;
-            }
-            SimpleByteCache.release(h);
-            changeState(State.OB_GOT_HXY);
-            _received = 0;
-            // their (Bob's) timestamp in seconds
-            _tsB = DataHelper.fromLong(hXY_tsB, HXY_SIZE, 4);
-            long now = _context.clock().now();
-            // rtt from sending #1 to receiving #2
-            long rtt = now - _con.getCreated();
-            // our (Alice's) timestamp in seconds
-            _tsA = (now + 500) / 1000;
-            _peerSkew = (now - (_tsB * 1000) - (rtt / 2) + 500) / 1000; 
-            if (_log.shouldLog(Log.DEBUG))
-                _log.debug(prefix()+"h(X+Y) is correct, skew = " + _peerSkew);
-
-            // the skew is not authenticated yet, but it is certainly fatal to
-            // the establishment, so fail hard if appropriate
-            long diff = 1000*Math.abs(_peerSkew);
-            if (!_context.clock().getUpdatedSuccessfully()) {
-                // Adjust the clock one time in desperation
-                // We are Alice, he is Bob, adjust to match Bob
-                _context.clock().setOffset(1000 * (0 - _peerSkew), true);
-                _peerSkew = 0;
-                if (diff != 0)
-                    _log.logAlways(Log.WARN, "NTP failure, NTCP adjusting clock by " + DataHelper.formatDuration(diff));
-            } else if (diff >= Router.CLOCK_FUDGE_FACTOR) {
-                _context.statManager().addRateData("ntcp.invalidOutboundSkew", diff);
-                _transport.markReachable(_con.getRemotePeer().calculateHash(), false);
-                // Only banlist if we know what time it is
-                _context.banlist().banlistRouter(DataHelper.formatDuration(diff),
-                                                   _con.getRemotePeer().calculateHash(),
-                                                   _x("Excessive clock skew: {0}"));
-                _transport.setLastBadSkew(_peerSkew);
-                fail("Clocks too skewed (" + diff + " ms)", null, true);
-                return;
-            } else if (_log.shouldLog(Log.DEBUG)) {
-                _log.debug(prefix()+"Clock skew: " + diff + " ms");
-            }
-
-            // now prepare and send our response
-            // send E(#+Alice.identity+tsA+padding+S(X+Y+Bob.identHash+tsA+tsB), sk, hX_xor_Bob.identHash[16:31])
-            int sigSize = XY_SIZE + XY_SIZE + HXY_SIZE + 4+4;//+12;
-            byte preSign[] = new byte[sigSize];
-            System.arraycopy(_X, 0, preSign, 0, XY_SIZE);
-            System.arraycopy(_Y, 0, preSign, XY_SIZE, XY_SIZE);
-            System.arraycopy(_con.getRemotePeer().calculateHash().getData(), 0, preSign, XY_SIZE + XY_SIZE, HXY_SIZE);
-            DataHelper.toLong(preSign, XY_SIZE + XY_SIZE + HXY_SIZE, 4, _tsA);
-            DataHelper.toLong(preSign, XY_SIZE + XY_SIZE + HXY_SIZE + 4, 4, _tsB);
-            // hXY_tsB has 12 bytes of padding (size=48, tsB=4 + hXY=32)
-            Signature sig = _context.dsa().sign(preSign, _context.keyManager().getSigningPrivateKey());
-
-            byte ident[] = _context.router().getRouterInfo().getIdentity().toByteArray();
-            // handle variable signature size
-            int min = 2 + ident.length + 4 + sig.length();
-            int rem = min % AES_SIZE;
-            int padding = 0;
-            if (rem > 0)
-                padding = AES_SIZE - rem;
-            byte preEncrypt[] = new byte[min+padding];
-            DataHelper.toLong(preEncrypt, 0, 2, ident.length);
-            System.arraycopy(ident, 0, preEncrypt, 2, ident.length);
-            DataHelper.toLong(preEncrypt, 2+ident.length, 4, _tsA);
-            if (padding > 0)
-                _context.random().nextBytes(preEncrypt, 2 + ident.length + 4, padding);
-            System.arraycopy(sig.getData(), 0, preEncrypt, 2+ident.length+4+padding, sig.length());
-
-            _prevEncrypted = new byte[preEncrypt.length];
-            _context.aes().encrypt(preEncrypt, 0, _prevEncrypted, 0, _dh.getSessionKey(),
-                                   _hX_xor_bobIdentHash, _hX_xor_bobIdentHash.length-AES_SIZE, preEncrypt.length);
-
-            changeState(State.OB_SENT_RI);
-            _transport.getPumper().wantsWrite(_con, _prevEncrypted);
-        }
-
-        // Read in message #4
-        if (_state == State.OB_SENT_RI && src.hasRemaining()) {
-            // we are receiving their confirmation
-
-            // recv E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev)
-            int off = 0;
-            if (_e_bobSig == null) {
-                // handle variable signature size
-                int siglen = _con.getRemotePeer().getSigningPublicKey().getType().getSigLen();
-                int rem = siglen % AES_SIZE;
-                int padding;
-                if (rem > 0)
-                    padding = AES_SIZE - rem;
-                else
-                    padding = 0;
-                _e_bobSig = new byte[siglen + padding];
-                if (_log.shouldLog(Log.DEBUG))
-                    _log.debug(prefix() + "receiving E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev) (remaining? " +
-                               src.hasRemaining() + ")");
-            } else {
-                off = _received;
-                if (_log.shouldLog(Log.DEBUG))
-                    _log.debug(prefix() + "continuing to receive E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev) (remaining? " +
-                               src.hasRemaining() + " off=" + off + " recv=" + _received + ")");
-            }
-            while (_state == State.OB_SENT_RI && src.hasRemaining()) {
-                _e_bobSig[off++] = src.get();
-                _received++;
-
-                if (off >= _e_bobSig.length) {
-                    changeState(State.OB_GOT_SIG);
-                    byte bobSig[] = new byte[_e_bobSig.length];
-                    _context.aes().decrypt(_e_bobSig, 0, bobSig, 0, _dh.getSessionKey(),
-                                           _e_hXY_tsB, HXY_TSB_PAD_SIZE - AES_SIZE, _e_bobSig.length);
-                    // ignore the padding
-                    // handle variable signature size
-                    SigType type = _con.getRemotePeer().getSigningPublicKey().getType();
-                    int siglen = type.getSigLen();
-                    // we don't need to do this if no padding!
-                    byte bobSigData[] = new byte[siglen];
-                    System.arraycopy(bobSig, 0, bobSigData, 0, siglen);
-                    Signature sig = new Signature(type, bobSigData);
-
-                    byte toVerify[] = new byte[XY_SIZE + XY_SIZE + HXY_SIZE +4+4];
-                    int voff = 0;
-                    System.arraycopy(_X, 0, toVerify, voff, XY_SIZE); voff += XY_SIZE;
-                    System.arraycopy(_Y, 0, toVerify, voff, XY_SIZE); voff += XY_SIZE;
-                    System.arraycopy(_context.routerHash().getData(), 0, toVerify, voff, HXY_SIZE); voff += HXY_SIZE;
-                    DataHelper.toLong(toVerify, voff, 4, _tsA); voff += 4;
-                    DataHelper.toLong(toVerify, voff, 4, _tsB); voff += 4;
-
-                    boolean ok = _context.dsa().verifySignature(sig, toVerify, _con.getRemotePeer().getSigningPublicKey());
-                    if (!ok) {
-                        _context.statManager().addRateData("ntcp.invalidSignature", 1);
-                        fail("Signature was invalid - attempt to spoof " + _con.getRemotePeer().calculateHash().toBase64() + "?");
-                    } else {
-                        if (_log.shouldLog(Log.DEBUG))
-                            _log.debug(prefix() + "signature verified from Bob.  done!");
-                        byte nextWriteIV[] = SimpleByteCache.acquire(AES_SIZE);
-                        System.arraycopy(_prevEncrypted, _prevEncrypted.length-AES_SIZE, nextWriteIV, 0, AES_SIZE);
-                        // this does not copy the nextWriteIV, do not release to cache
-                        // We are Alice, he is Bob, clock skew is Bob - Alice
-                        // skew in seconds
-                        _con.finishOutboundEstablishment(_dh.getSessionKey(), _peerSkew, nextWriteIV, _e_bobSig);
-                        changeState(State.VERIFIED);
-                        if (src.hasRemaining()) {
-                            // process "extra" data
-                            // This is fairly common for outbound, where Bob may send his updated RI
-                            if (_log.shouldInfo())
-                                _log.info("extra data " + src.remaining() + " on " + this);
-                            _con.recvEncryptedI2NP(src);
-                        }
-                        releaseBufs(true);
-                        // if socket gets closed this will be null - prevent NPE
-                        InetAddress ia = _con.getChannel().socket().getInetAddress();
-                        if (ia != null)
-                            _transport.setIP(_con.getRemotePeer().calculateHash(), ia.getAddress());
-                    }
-                    return;
-                }
-            }
-        }
-
-        // check for remaining data
-        if ((_state == State.VERIFIED || _state == State.CORRUPT) && src.hasRemaining()) {
-            if (_log.shouldWarn())
-                _log.warn("Received unexpected " + src.remaining() + " on " + this, new Exception());
-        }
-    }
-
-    /**
-     * We are Alice.
-     * We are establishing an outbound connection, so prepare ourselves by
-     * queueing up the write of the first part of the handshake
-     * This method sends message #1 to Bob.
-     *
-     * @throws IllegalStateException
-     */
-    @Override
-    public synchronized void prepareOutbound() {
-        if (_state == State.OB_INIT) {
-            if (_log.shouldLog(Log.DEBUG))
-                _log.debug(prefix() + "send X");
-            byte toWrite[] = new byte[XY_SIZE + _hX_xor_bobIdentHash.length];
-            System.arraycopy(_X, 0, toWrite, 0, XY_SIZE);
-            System.arraycopy(_hX_xor_bobIdentHash, 0, toWrite, XY_SIZE, _hX_xor_bobIdentHash.length);
-            changeState(State.OB_SENT_X);
-            _transport.getPumper().wantsWrite(_con, toWrite);
-        } else {
-            throw new IllegalStateException(prefix() + "unexpected prepareOutbound()");
-        }
-    }
-
-    /**
-     *  Only call once. Caller must synch.
-     *  @since 0.9.16
-     */
-    @Override
-    protected void releaseBufs(boolean isVerified) {
-        super.releaseBufs(isVerified);
-        Arrays.fill(_Y, (byte) 0);
-        SimpleByteCache.release(_Y);
-    }
-}