[Twisted-Python] pb.Copyable, round trip objects, and untrusted clients

David Ripton dripton at ripton.net
Thu May 20 23:46:53 EDT 2004

On 2004.05.20 13:07:06 +0000, Christopher Armstrong wrote:
> Well, your example didn't look like it had much use for using 
> particularly complex state going both ways. Maybe you can explain this 
> game-choosing process in a bit more detail? Without knowing any more, 
> letting the user choose which game he wants to play sounds like it 
> shouldn't be more complex than the server giving the client a list of 
> strings of game-names (like "Bob's l33t Server") and the client sending 
> back "Bob's l33t Server" in a request later on.

The full game has much more complex state, which I will resist spelling
out in detail.  I was just trying to show a minimal example. 

Yes, that particular event would only really require sending a list of 
dicts or tuples, with each dict or tuple holding the ~6 attributes of 
a single game that need to be displayed in a table, from server to 
client.  And then sending just the name or id of the one game that 
the user wants to join back to the server.  (Another event would send a
different subset of the game state.)

But there is a (small) code size cost to pass just the attributes of 
the Game class that are needed for that GUI display.  Passing the
whole Game class (censored in a global rather than event-specific way)
is simpler.  And if I decide to add another column to that table,
passing the whole object probably doesn't require a change to the 
remote call arguments, while passing the minimal bits does.  And it just
feels like better OO design to pass a whole object rather than ripping
out bits and pieces of it.

None of which matters much, if passing just dicts and strings and ints 
is secure, and passing copies of complex objects is not.  (This seems to
happen in every "distributed object system" I try.  Basic RPC works
[except in Java RMI, which appears to be an April Fool's joke]; 
anything more complex works for "Hello World" then falls over in


Passing globally reusable game state from server to clients in a 
consistent, systematic way makes a lot more sense than providing 
minimal ad hoc local context for each distinct action.

There are two opposite ways to do this, which in the end generate 
mostly the same result.

A. Apply the (error-checked) action on the server, then use Copyable or 
Cacheable to copy the new state (except the hidden parts) over to the 
clients.  The clients hold a slightly delayed mirror image of the 
server's state.

B. Initialize separate objects of the same classes on the server and 
each client to the same starting state, then pass the same action that 
just ran on the server to each client (except hidden events).  If you 
assume that all actions are deterministic (meaning die rolls are 
separate actions), the same code running the same actions will 
generate the same end state.  The server and client are now more
like blind synchronized swimmers listening to the same coach.

B is more work up front.  However, assuming applying an action to a 
state to get the next state is cheap (and cheaply reversable), I think 
it's superior.  First, it uses more CPU on clients and less network 
bandwidth, which is a good tradeoff.  Second, you have the full event 
stream so you can easily undo/redo, either for real or to just step 
through previous game history.  Finally, client -> server traffic has 
to be small (attempted) actions, not full object dumps, because the 
server doesn't trust the client, so it has the beauty of symmetry.

> IOW, the poster who said that your second and third points are basically 
> the same is correct; the API you're designing needs to be looked at 
> specifically to see the optimal secure/easy solution.


Upon further review, I'm back to throwing minimal action objects both
ways, eschewing Copyable and Cacheable completely in favor of manual
solutions involving dicts and lists of strings and ints.  Which is 
where I was a couple of weeks ago.  The grass is always greener...

The security issues in Copyable / Cacheable should knock me off this 
particular fence for long enough to have enough code written that I'm
unwilling to climb back up.

Thanks guys.

David Ripton    dripton at ripton.net

More information about the Twisted-Python mailing list