OK, here is an idea I have been working on for a while. It's a bit long, so please 
respond to it piecemeal.

Here is a solution that I think can make NGrouting work correctly, aid load balancing, 
lower Query rejecting, prevent users from overloading the network, and end world 
hunger. Well, scratch that last one...

The idea is this: We eliminate the notion of HTL. Instead we replace it with a trust 
based system with TTL.
Each node has a amount of trust in each other node that it connects with. This is 
initially 0. Everytime a node makes a request it gives a trust level it is willing to 
spend in order to have it fulfilled. If the node does not have that much trust built 
up, it is reduced to the maximum that it has.

Trust is measured in seconds. Each node will forward a request with the Trust level it 
came in with minus any time that has elapsed since then. Upon the successful return of 
data a node is rewarded by the requesting node giving it trust.

The amount of trust rewarded is equal to:
(2 * TTL^2 - Time^2)^(1/2)
I selected this because it insures that:
1. There is a finite limit to the amount of trust that can be gained.
2. There is a finite time beyond which returning the data is no longer valuable.
3. Over performance rewards all the nodes in the path, by increasing their net trust 
level gain. Not just the node that overpreformed.
4. Under performance reduces the regard for all nodes in the request path, not just 
the node that underpreformed.
5. The bonus or penalty for performance it proportional to how close one is to where 
the difference happened.


Here is some pseudo code for an implementation. Could someone please turn this in to 
real Java code that would work in Freenet, and add it to the experiential branch?

//Incoming request for a key with a TTL.
//The code is basically the same for an insert. The only differences are the same as 
they are now.

void processRequest(Node node,Key key, TTL ttl)
{
        float load=getCurrentLoad(); //Check our current load.

          //Tenitively subtract the maximum amount of credit that the node could owe 
us.
          //Subtract returns the amount it took away. (never more than there was there)
        float credit = node.credit.subtract(ttl * 1.414214);
          //Check how much credit the node has. And reduce the request if necessary
        if ( credit < ttl * 1.414214 ) ttl = credit / 1.414214;

        if ( Node.DataStore.Contians(key) )
        {
                //return the data...
        }
        else if ( shouldAccept(load, credit, this) )
        {
                //Accept the query and pass our load back to the requesting node.
                QuerryAccept(node,key,load);
                route(key,ttl);
        }
        else
        {
                //Reject the query and pass our load and estimated retrieval time back 
to the requesting node.
                QuerryReject(node,key,load,tSuccess( r.key ));
        }
}

bool shouldAccept(float load, Request r)
{
        if(load<1) return true;

          // Calculate reject presentage
        float rejectRate = (1 - load) / load;
          // Hum this may not be aggressive enough.
          // perhaps instead it should be ( 1 - load ) that way we overshoot a little.

        if (rejectRate >= 1) return false;

          // Compair running average of how much credit we gain per request to current 
estimate.

          // CreditGainPresentile returns the given percentile of how much credit we 
are currently gaining per request.
          // To be safe it should round up if necessary.
        if ( ExpectedNetCreditGain(r) < CreditGainPresentile(rejectRate) ) return 
false;
        else return true;
}

float ExpectedNetCreditGain(Request r)
{
        //Use global estimator tSuccess which works exactly as it does now
        float returnTime = tSuccess( r.key );

        //SendTime and ReceveTime are global running averages of the trip time for all 
the nodes in the routing table.
        //sendTime estimates:           The time it will take for our request to land 
on the next node.
        //receveTime estimates  The time it will take for the other node to forward us 
the data once it gets it.
        //Note: for both of the above, we only want to be sure to have a good average 
bound on the number,
        //              so we should probably average the roundtrip time for a message 
of the appropriate size.

        return Max( 0, squareRoot( 2 * r.ttl^2 - returnTime^2 ) -
                                squareRoot( 2 * ( r.ttl - r.elapsedTime() -sendTime 
-receveTime )^2 - ( returnTime - receveTime )^2 ) );
}

void requestComplete( Node requester, Node provider, TTL requesterTTL, TTL 
providerTTL, float requesterTime, float providerTime, bool successful)
{
        if (successful)
        {
                if ( requester != me )
                {       //add back the amount we took away minus the amount they owe 
us.
                        requester.trust.add( requesterTTL * 1.414214 - Max( 0 , 
squareRoot( 2 * requesterTTL^2 - requesterTime^2 ) ) );
                        requester.trustInMe.add(  Max( 0 , squareRoot( 2 * 
requesterTTL^2 - requesterTime^2 ) ) );
                }
                //Pay out. Pay up.
                if ( provider != me )
                {
                        provider.trust.add( Max( 0 , squareRoot( 2 * providerTTL^2 - 
providerTime^2 ) ) );
                        //add back the amount they took away minus the amount we owe 
them.
                        provider.trustInMe.add( providerTTL * 1.414214 - Max( 0 , 
squareRoot( 2 * providerTTL^2 - providerTime^2 ) ) );
                }
        }
        else
        {
                //everything already subtracted. Nothing to add.
        }
}

Routing works the same as it does now, except we do not need to estimate the 
probability of DNF. This is a BIG gain! This takes all the alchemy out of NGrouting. 
It is assumed that all node could EVENTUALLY find the data, but they will reject the 
request if they cannot do so profitably. So we just go through all the nodes and sort 
them by the largest  ( estimatedTime - ttl ) where ttl is:
ttl = Min ( ttl - elapsedTime() - sendTime - receveTime() , them.trustInMe );
If they don't think that they can they can process the request profitably, then they 
will reject it. If our first node rejects, goto the second then the third, and so 
forth. The ttl we froward will become smaller and smaller. At some point even if the 
node responds in the predicted time, we will not make a profit. At this point we QR.

Query rejects contain both the load and the estimated time it would have taken to get 
the data.
If the load is high, back off.
If the time is higher than our estimated time of their performance. Add their 
estimated time into our estimator, as though they had succeeded with that time.
If their estimated time is lower, back off.

Ideally this would encompass inserts too. But I can't think of a really good way to do 
this. So in the mean time I would suggest that they keep the HTL. Then have the TTL == 
HTL so each node to forward it gains one TTL (net).

Problems this solves.
1. No single user can abuse the network.
2. Users that donate more in their data store or bandwidth get rewarded by better 
network performance.
3. NGrouting can have accurate estimates because it does not need to know pDNF which 
previously was calculated without taking into account the HTL.
4. All data will ether be found or fail in a fixed time frame.
5. Requesting Non existant keys cannot DOS the network
6. We learn about a nodes specialization even if the request does not work.
7. EVERYTHING can now we done non-alchemisticly. (Data store contents, Caching, 
Rejecting, etc.) Nodes maximize for profit.
8. Current overload and routing problems go away.


Debatable issues:
Is there a better reward function? The one I chose rewards the person that had the 
data a LOT. If we use it, frowarding requests will not be nearly as profitable as a 
large data store. If someone can come up with a different formula that meets all of 
the criteria I specified then please do. I played around with some parabolic 
functions, those could work too...

What about decreasing the TTL for credit gain? A malicious node could deliberately 
decrease the TTL more than necessary. However in doing so, they decrease the chance of 
the request succeeding, and as a result under preform. I am of the opinion that this 
is a non-issue, because the amount of trust they grant another node is never 
transmitted, it is only calculated on their node. So they could simply never give 
anyone credit, or give each person less, but if they do, they gain nothing. Others 
would simply think they were preforming poorly, so they would have less potential to 
earn credit.

Should we trust as much as we do? Originally I thought making Freenet into a positive 
trust biased network would, require creating estimators for the relative value of each 
nodes trust, and then calculating the relative gain between each pair of nodes for 
each request, before we can even determine if the request was worth while. First I 
realized that, if we have X trust in another node, that means that we owe them X. They 
have already fulfilled our request for X, so we should pay them back regardless of 
what we gain. However when we become loaded, we want to process the requests that will 
be the most valueable to us. In this implementation I assume that the relative value 
of each nodes data is proportional to the amount of time it offers us out of the 
amount that we currently owe it biased on as of yet unrepaied requests that it has 
processed on our behalf. Although this is alchemistic, it is not unreasonable. If the 
node has given us data before, and it says it will allow us
  to ask for the same amount again, if we repay it by answering this request, it is 
not unreasonable to trust it. This greatly simplifies implementation.

Injecting trust into the system. Originally I had proposed not charding for our 
services if we are idle. Then someone on the list pointed out that this made the 
system vulnerable to attack. Multiple conspiring nodes could suck up all of a nodes 
idle time and use it to gain a huge amount of trust in that node. So I have a new 
proposal. We do something similar to what we are doing now. That is, every so often, 
say 30 sec, we find the node in our table that we have not spoken with the longest. If 
we don't have enough trust in that node to get an average query through, nor they with 
us, we get a key that we successfully fetched recently, and ask it for that key. Now, 
because we have no trust with that node, we send it with a TTL of 0. This means if the 
node is at all busy, it will drop it. However if it is not, or just really wants to 
talk to you, then it forwards it, still with an TTL of 0 along it's route. TTL of 0 is 
a special designation. It has no expiration time it instead 
 dies after a certain large number of hops, say 25. So it is forwarded whenever the 
node that has it decides it is not busy enough to process it. When the data eventually 
comes back, the provider does not subtract any credit from the requester. BUT the 
requester gives the provider some constant amount of credit. Say 5. Then when the data 
gets back to the original node, the node that it asked for the data, now has enough 
credit with it to ask it for any single key. So starting a request with a TTL of 0 is 
like saying, "Look, I don't have enough credit with you do ask for data, and you don't 
have enough with me, so if you answer this request, I'll grant you one request for 
Free."

TTL = 0 could also be used a form of a passive request. New nodes of course would have 
to make all their requests at TTL = 0 for a time. To help accelerate this, we could 
also give a node connecting to us, a piece of data from our store. Say a somewhat 
popular one. Then we could remove it from our store (If they have held on to it 
properly), and send incoming requests for the data to it. So we gain, but they are 
regarded for holding the data. This would get new node trust fast. However it does 
require other nodes to be generous and reduce their potential for future trust gain 
somewhat.

Any additional proposals for speeding the intrigration of new nodes are welcome.

Things that need to be done:
-Flamewar
-Make the code work in Freenet.
-Make the dozzens of other changes in the code necessary to get all this together.
-Ether fix the data size, or make all the estimators smart enough to deal with varring 
sizes at various trust levels. (I suspect this will be easy for someone, but I have no 
idea how to do it.)
-Make traffic more symmetric. (Implement multiplexing?)
-Commit this to experiential.
-Set up a test network.
-See how well it works.
-Make data store maximize for profit.
-Make bigger test.
-Merge.
*Anything I forgot.

Unfortunately I won't be able to read my e-mail for several days. But I hope to see 
some good discussion and progress when I get back.

_______________________________________________
Devl mailing list
[EMAIL PROTECTED]
http://dodo.freenetproject.org/cgi-bin/mailman/listinfo/devl

Reply via email to