Max HTL = 10 HTL might not be decremented at max and/or min - each node decides once at startup, to prevent statistical attacks 25% chance of decrementing at min, 50% chance of decrementing at max Handling a data request: If same id was recently completed, send RejectedLoop and return If overloaded, send RejectedOverload and return If same id is in progress, send RejectedLoop and return (Request accepted) If this is the closest node so far, update closest location and reset htl Send Accepted If data is in store or cache: If key is an SSK, send SSKDataFound and SSKPubKey if requested, return If key is a CHK, start a new BlockTransmitter and return If there's a transferring RequestSender with the same key, coalesce Otherwise if htl==0, send DataNotFound and return If there's a RequestSender with the same key and htl, coalesce If not coalesced, create a new RequestSender Wait for RequestSender to finish If RejectedOverload, forward it If incoming transfer starts, start a new BlockTransmitter (RequestSender finished) If TIMED_OUT, INTERNAL_ERROR or VERIFY_FAILURE, send local RejectedOverload If TRANSFER_FAILED, return (previous hop will detect failure) If DATA_NOT_FOUND, send DataNotFound If ROUTE_NOT_FOUND, send RouteNotFound If SUCCESS: If key is an SSK, send SSKDataFound, SSKData and SSKPubKey if requested Handling a CHK insert request: If same id was recently completed, send RejectedLoop and return If overloaded, send RejectedOverload and return If same id is in progress, send RejectedLoop and return (Insert accepted) If this is the closest node so far, update closest location and reset htl Send Accepted Wait 10 seconds for a DataInsert If peer disconnects, return If wait times out: Send RejectedTimeout and InsertTransfersCompleted(true), return (DataInsert received) If htl>0: If there's a CHKInsertSender with the same key and htl, coalesce Otherwise start a new CHKInsertSender Start a new DataReceiver If htl==0, send InsertReply and finish Wait for CHKInsertSender to finish If incoming transfer fails, kill CHKInsertSender and finish If RejectedOverload, forward it (CHKInsertSender finished) If TIMED_OUT or INTERNAL_ERROR, send local RejectedOverload and finish If ROUTE_NOT_FOUND, send RouteNotFound and finish If SUCCESS, send InsertReply and finish Otherwise (unknown status code) send local RejectedOverload and finish Finish: Wait for transfers to complete If incoming transfer failed, send DataInsertRejected and return Verify data or send DataInsertRejected and return Store data and send InsertTransfersCompleted(transferTimedOut) Handling an SSK insert request: If same id was recently completed, send RejectedLoop and return If overloaded, send RejectedOverload and return If same id is in progress, send RejectedLoop and return (Insert accepted) If this is the closest node so far, update closest location and reset htl Send Accepted, indicating whether public key is needed If public key is needed: Wait 10 seconds for previous hop to send public key If peer disconnects, return If wait times out, return (no RejectedTimeout?) If SSKPubKey, send SSKPubKeyAccepted (Public key received) Verify insert or send DataInsertRejected and return If data is in store or cache and doesn't match insert: Send SSKDataFound with old data Continue to insert old data instead of new If htl==0, send InsertReply, store data and return If there's a SSKInsertSender with the same key and htl, coalesce Otherwise start a new SSKInsertSender Wait for SSKInsertSender to finish If RejectedOverload, forward it If insert collides at next hop: Forward SSKDataFound to previous hop (SSKInsertSender finished) If TIMED_OUT or INTERNAL_ERROR: Send local RejectedOverload, store data and return If ROUTE_NOT_FOUND, send RouteNotFound, store data and return If SUCCESS, send InsertReply, store data and return Otherwise (unknown status code) send local RejectedOverload RequestSender loop: If htl==0, return ROUTE_NOT_FOUND If no remaining peers, return ROUTE_NOT_FOUND Remove next hop from list of potential next hops If next hop is further from target than closest location, decrement htl Send request to next hop Wait 5 seconds for Accepted, RejectedLoop or RejectedOverload If peer disconnects, try another peer If wait times out, back off and try another peer If local RejectedOverload, back off and try another peer If non-local RejectedOverload, forward it If RejectedLoop, try another peer If anything other than Accepted, try another peer (Request accepted) Wait 60 seconds for DataNotFound, RouteNotFound, RejectedOverload, SSKPubKey, CHKDataFound or SSKDataFound If peer disconnects, try another peer If wait times out, back off and return TIMED_OUT If DataNotFound, return DATA_NOT_FOUND If RouteNotFound, let htl=min(htl,newHtl) and try another peer If local RejectedOverload, back off and try another peer If non-local RejectedOverload, forward it If SSKPubKey: If key is not an SSK, try another peer Verify public key or try another peer If data has not been received, continue to wait Verify data or return VERIFY_FAILURE Return SUCCESS If CHKDataFound: If key is not a CHK, try another peer Receive data or return TRANSFER_FAILED Verify data or return VERIFY_FAILURE Return SUCCESS If SSKDataFound: If key is not an SSK, try another peer If public key has not been received, continue to wait Verify data or return VERIFY_FAILURE Return SUCCESS SSKInsertSender loop: If htl==0, return SUCCESS If no remaining peers, return ROUTE_NOT_FOUND Remove next hop from list of potential next hops If next hop is further from target than closest location, decrement htl Send insert to next hop Wait 10 seconds for SSKAccepted, RejectedLoop or RejectedOverload If peer disconnects, try another peer If wait times out, back off and try another peer If local RejectedOverload, back off and try another peer If non-local RejectedOverload, forward it If RejectedLoop, try another peer If anything other than Accepted, try another peer (Insert accepted) If next hop needs public key: Send SSKPubKey Wait 10 seconds for SSKPubKeyAccepted If wait times out, back off and try another peer Wait 60 seconds for InsertReply, RejectedOverload, RouteNotFound, DataInsertRejected or SSKDataFound (collision, returns old data) If peer disconnects, try another peer If wait times out, back off and return TIMED_OUT If local RejectedOverload, back off and try another peer If non-local RejectedOverload, forward it If RouteNotFound, let htl=min(htl,newHtl) and try another peer If DataInsertRejected, try another peer If SSKDataFound (collision): If new data equals old data (which it shouldn't), try another peer Verify old data or try another peer Let hasCollided=true, hasRecentlyCollided=true, continue to wait If anything other than InsertReply, return INTERNAL_ERROR Return SUCCESS CHKInsertSender loop: If htl==0, return SUCCESS If no remaining peers, return ROUTE_NOT_FOUND Remove next hop from list of potential next hops If next hop is further from target than closest location, decrement htl Send insert to next hop If incoming transfer fail has failed, return Wait 10 seconds for Accepted, RejectedLoop or RejectedOverload If wait times out, back off and try another peer If incoming transfer has failed, return If local RejectedOverload, back off and try another peer If non-local RejectedOverload, forward it If RejectedLoop, try another peer If anything other than Accepted, try another peer (Insert accepted) If incoming transfer has failed, return Send DataInsert In another thread: Start outgoing transfer If there's no CompletionWaiter running, start a new CompletionWaiter (Transfer started) Wait 120 seconds for InsertReply, RouteNotFound, DataInsertRejected or RejectedOverload If incoming transfer has failed, return If peer disconnects, try another peer If wait times out, back off and return TIMED_OUT If local RejectedOverload, back off and try another peer If non-local RejectedOverload, forward it If RouteNotFound, let htl=min(htl,newHtl) and try another peer If DataInsertRejected, try another peer If anything other than InsertReply, return INTERNAL_ERROR Return SUCCESS CompletionWaiter: Wait for insert to finish Wait a further 120 seconds for InsertTransfersCompleted from all next hops If wait times out, let transferTimedOut=true If InsertTransfersCompleted indicates a timeout, let transferTimedOut=true