From e02845076da812e5c815ada5a15e373fea65788c Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Tue, 10 Nov 2009 01:24:39 +0000
Subject: [PATCH]     * Netdb StoreJob, FloodfillVerifyStoreJob:       - Fix
 bug where reply selector wasn't registered for         routerinfo stores, so
 we didn't get stats, and         we kept retrying. This also prevented
 verification         and profile updates for routerinfo stores.         This
 bug was introduced 4 years ago by the change to         store routerinfos
 directly.       - Add dbStoreSuccessful() to profile, and have FVSJ        
 call it or dbStoreFailed() as appropriate to give         credit or blame to
 the floodfill we stored to.       - Don't let FVSJ verify using the peer we
 stored to

---
 .../src/net/i2p/router/ProfileManager.java    |  7 ++++
 .../networkdb/kademlia/FloodfillStoreJob.java | 14 +++++---
 .../kademlia/FloodfillVerifyStoreJob.java     | 34 +++++++++++++++----
 .../router/networkdb/kademlia/StoreJob.java   |  8 +++--
 .../router/networkdb/kademlia/StoreState.java |  5 +++
 .../peermanager/ProfileManagerImpl.java       | 28 ++++++++++++---
 6 files changed, 79 insertions(+), 17 deletions(-)

diff --git a/router/java/src/net/i2p/router/ProfileManager.java b/router/java/src/net/i2p/router/ProfileManager.java
index b19bbc4c39..f829bdaeb4 100644
--- a/router/java/src/net/i2p/router/ProfileManager.java
+++ b/router/java/src/net/i2p/router/ProfileManager.java
@@ -136,6 +136,13 @@ public interface ProfileManager {
      */
     void dbStoreSent(Hash peer, long responseTimeMs);
     
+    /**
+     * Note that we confirmed a successful send of db data to 
+     * the peer.
+     *
+     */
+    void dbStoreSuccessful(Hash peer);
+
     /**
      * Note that we were unable to confirm a successful send of db data to 
      * the peer, at least not within our timeout period
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java
index 2c9eeb5b62..294093627b 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java
@@ -8,6 +8,8 @@ package net.i2p.router.networkdb.kademlia;
  *
  */
 
+import java.util.Iterator;
+import java.util.NoSuchElementException;
 import java.util.Set;
 
 import net.i2p.data.DataStructure;
@@ -55,16 +57,20 @@ class FloodfillStoreJob extends StoreJob {
             // Get the time stamp from the data we sent, so the Verify job can meke sure that
             // it finds something stamped with that time or newer.
             long published = 0;
-            boolean isRouterInfo = false;
             DataStructure data = _state.getData();
-            if (data instanceof RouterInfo) {
+            boolean isRouterInfo = data instanceof RouterInfo;
+            if (isRouterInfo) {
                 published = ((RouterInfo) data).getPublished();
-                isRouterInfo = true;
             } else if (data instanceof LeaseSet) {
                 published = ((LeaseSet) data).getEarliestLeaseDate();
             }
+            // we should always have exactly one successful entry
+            Hash sentTo = null;
+            try {
+                sentTo = _state.getSuccessful().iterator().next();
+            } catch (NoSuchElementException nsee) {}
             getContext().jobQueue().addJob(new FloodfillVerifyStoreJob(getContext(), _state.getTarget(),
-                                                                       published, isRouterInfo, _facade));
+                                                                       published, isRouterInfo, sentTo, _facade));
         }
     }
     
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillVerifyStoreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillVerifyStoreJob.java
index d31ece2233..bfc26d902b 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillVerifyStoreJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillVerifyStoreJob.java
@@ -26,6 +26,7 @@ public class FloodfillVerifyStoreJob extends JobImpl {
     private Log _log;
     private Hash _key;
     private Hash _target;
+    private Hash _sentTo;
     private FloodfillNetworkDatabaseFacade _facade;
     private long _expiration;
     private long _sendTime;
@@ -34,12 +35,16 @@ public class FloodfillVerifyStoreJob extends JobImpl {
     
     private static final int VERIFY_TIMEOUT = 10*1000;
     
-    public FloodfillVerifyStoreJob(RouterContext ctx, Hash key, long published, boolean isRouterInfo, FloodfillNetworkDatabaseFacade facade) {
+    /**
+     *  @param sentTo who to give the credit or blame to, can be null
+     */
+    public FloodfillVerifyStoreJob(RouterContext ctx, Hash key, long published, boolean isRouterInfo, Hash sentTo, FloodfillNetworkDatabaseFacade facade) {
         super(ctx);
         _key = key;
         _published = published;
         _isRouterInfo = isRouterInfo;
         _log = ctx.logManager().getLog(getClass());
+        _sentTo = sentTo;
         _facade = facade;
         // wait 10 seconds before trying to verify the store
         getTiming().setStartAfter(ctx.clock().now() + VERIFY_TIMEOUT);
@@ -70,22 +75,28 @@ public class FloodfillVerifyStoreJob extends JobImpl {
             return;
         }
         
+        if (_log.shouldLog(Log.INFO))
+            _log.info("Starting verify (stored " + _key + " to " + _sentTo + "), asking " + _target);
         _sendTime = getContext().clock().now();
         _expiration = _sendTime + VERIFY_TIMEOUT;
         getContext().messageRegistry().registerPending(new VerifyReplySelector(), new VerifyReplyJob(getContext()), new VerifyTimeoutJob(getContext()), VERIFY_TIMEOUT);
         getContext().tunnelDispatcher().dispatchOutbound(lookup, outTunnel.getSendTunnelId(0), _target);
     }
     
+    /** todo pick one close to the key */
     private Hash pickTarget() {
         FloodfillPeerSelector sel = (FloodfillPeerSelector)_facade.getPeerSelector();
-        List peers = sel.selectFloodfillParticipants(_facade.getKBuckets());
+        List<Hash> peers = sel.selectFloodfillParticipants(_facade.getKBuckets());
         Collections.shuffle(peers, getContext().random());
-        if (peers.size() > 0)
-            return (Hash)peers.get(0);
+        for (int i = 0; i < peers.size(); i++) {
+            Hash rv = peers.get(i);
+            if (!rv.equals(_sentTo))
+                return rv;
+        }
         
         if (_log.shouldLog(Log.WARN))
-            _log.warn("No peers to verify floodfill with");
-        return null;
+            _log.warn("No other peers to verify floodfill with, using the one we sent to");
+        return _sentTo;
     }
     
     private DatabaseLookupMessage buildLookup() {
@@ -140,7 +151,11 @@ public class FloodfillVerifyStoreJob extends JobImpl {
                 if (success) {
                     // store ok, w00t!
                     getContext().profileManager().dbLookupSuccessful(_target, delay);
+                    if (_sentTo != null)
+                        getContext().profileManager().dbStoreSuccessful(_sentTo);
                     getContext().statManager().addRateData("netDb.floodfillVerifyOK", delay, 0);
+                    if (_log.shouldLog(Log.INFO))
+                        _log.info("Verify success");
                     return;
                 }
                 if (_log.shouldLog(Log.WARN))
@@ -149,8 +164,12 @@ public class FloodfillVerifyStoreJob extends JobImpl {
                 // assume 0 old, all new, 0 invalid, 0 dup
                 getContext().profileManager().dbLookupReply(_target,  0,
                                 ((DatabaseSearchReplyMessage)_message).getNumReplies(), 0, 0, delay);
+                if (_log.shouldLog(Log.WARN))
+                    _log.warn("Verify failed - DSRM");
             }
             // store failed, boo, hiss!
+            if (_sentTo != null)
+                getContext().profileManager().dbStoreFailed(_sentTo);
             getContext().statManager().addRateData("netDb.floodfillVerifyFail", delay, 0);
             resend();
         }        
@@ -173,7 +192,10 @@ public class FloodfillVerifyStoreJob extends JobImpl {
         }
         public String getName() { return "Floodfill verification timeout"; }
         public void runJob() { 
+            // don't know who to blame (we could have gotten a DSRM) so blame both
             getContext().profileManager().dbLookupFailed(_target);
+            if (_sentTo != null)
+                getContext().profileManager().dbStoreFailed(_sentTo);
             getContext().statManager().addRateData("netDb.floodfillVerifyTimeout", getContext().clock().now() - _sendTime, 0);
             resend(); 
         }
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java
index ce8dee1931..d189068b5d 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java
@@ -269,6 +269,7 @@ class StoreJob extends JobImpl {
             sendStoreThroughGarlic(msg, peer, expiration);
         } else {
             getContext().statManager().addRateData("netDb.storeRouterInfoSent", 1, 0);
+            // todo - shorter expiration for direct?
             sendDirect(msg, peer, expiration);
         }
     }
@@ -298,6 +299,7 @@ class StoreJob extends JobImpl {
         m.setPriority(STORE_PRIORITY);
         m.setReplySelector(selector);
         m.setTarget(peer);
+        getContext().messageRegistry().registerPending(m);
         getContext().commSystem().processMessage(m);
     }
     
@@ -324,8 +326,8 @@ class StoreJob extends JobImpl {
             //if (_log.shouldLog(Log.DEBUG))
             //    _log.debug(getJobId() + ": Sending tunnel message out " + outTunnelId + " to " 
             //               + peer.getIdentity().getHash().toBase64());
-            TunnelId targetTunnelId = null; // not needed
-            Job onSend = null; // not wanted
+            //TunnelId targetTunnelId = null; // not needed
+            //Job onSend = null; // not wanted
         
             SendSuccessJob onReply = new SendSuccessJob(getContext(), peer, outTunnel, msg.getMessageSize());
             FailedJob onFail = new FailedJob(getContext(), peer, getContext().clock().now());
@@ -426,7 +428,7 @@ class StoreJob extends JobImpl {
             
             sendNext();
         }
-        public String getName() { return "Kademlia Store Peer Failed"; }
+        public String getName() { return "Kademlia Store Send Failed"; }
     }
 
     /**
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/StoreState.java b/router/java/src/net/i2p/router/networkdb/kademlia/StoreState.java
index eaa51edb0b..84e955c9ca 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/StoreState.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/StoreState.java
@@ -11,6 +11,9 @@ import net.i2p.data.DataStructure;
 import net.i2p.data.Hash;
 import net.i2p.router.RouterContext;
 
+/**
+ *  Todo: remove exploratory
+ */
 class StoreState {
     private RouterContext _context;
     private Hash _key;
@@ -63,6 +66,7 @@ class StoreState {
             return (Set<Hash>)_successfulPeers.clone(); 
         }
     }
+    /** @deprecated unused */
     public Set<Hash> getSuccessfulExploratory() { 
         synchronized (_successfulExploratoryPeers) {
             return (Set<Hash>)_successfulExploratoryPeers.clone(); 
@@ -124,6 +128,7 @@ class StoreState {
         return rv;
     }
 
+    /** @deprecated unused */
     public long confirmedExploratory(Hash peer) {
         long rv = -1;
         synchronized (_pendingPeers) {
diff --git a/router/java/src/net/i2p/router/peermanager/ProfileManagerImpl.java b/router/java/src/net/i2p/router/peermanager/ProfileManagerImpl.java
index 2b97e2df78..69088caaa7 100644
--- a/router/java/src/net/i2p/router/peermanager/ProfileManagerImpl.java
+++ b/router/java/src/net/i2p/router/peermanager/ProfileManagerImpl.java
@@ -89,6 +89,7 @@ public class ProfileManagerImpl implements ProfileManager {
     /**
      * Note that a router explicitly rejected joining a tunnel.  
      *
+     * @param responseTimeMs ignored
      * @param severity how much the peer doesnt want to participate in the 
      *                 tunnel (large == more severe)
      */
@@ -249,9 +250,30 @@ public class ProfileManagerImpl implements ProfileManager {
      * Note that we've confirmed a successful send of db data to the peer (though we haven't
      * necessarily requested it again from them, so they /might/ be lying)
      *
-     * This will force creation of DB stats
+     * This is not really interesting, since they could be lying, so we do not
+     * increment any DB stats at all. On verify, call dbStoreSuccessful().
+     *
+     * @param responseTimeMs ignored
      */
     public void dbStoreSent(Hash peer, long responseTimeMs) {
+        PeerProfile data = getProfile(peer);
+        if (data == null) return;
+        long now = _context.clock().now();
+        data.setLastHeardFrom(now);
+        data.setLastSendSuccessful(now);
+        //if (!data.getIsExpandedDB())
+        //    data.expandDBProfile();
+        //DBHistory hist = data.getDBHistory();
+        //hist.storeSuccessful();
+    }
+    
+    /**
+     * Note that we've verified a successful send of db data to the floodfill peer
+     * by querying another floodfill.
+     *
+     * This will force creation of DB stats
+     */
+    public void dbStoreSuccessful(Hash peer) {
         PeerProfile data = getProfile(peer);
         if (data == null) return;
         long now = _context.clock().now();
@@ -261,8 +283,6 @@ public class ProfileManagerImpl implements ProfileManager {
             data.expandDBProfile();
         DBHistory hist = data.getDBHistory();
         hist.storeSuccessful();
-        // we could do things like update some sort of "how many successful stores we've sent them"...
-        // naah.. dont really care now
     }
     
     /**
@@ -277,7 +297,7 @@ public class ProfileManagerImpl implements ProfileManager {
         if (!data.getIsExpandedDB())
             data.expandDBProfile();
         DBHistory hist = data.getDBHistory();
-        hist.storeSuccessful();
+        hist.storeFailed();
         // we could do things like update some sort of "how many successful stores we've
         // failed to send them"...
     }
-- 
GitLab