Walnut/Persistent Secure Distributed Computing
From Erights
Persistent Secure Distributed Computing
There is a problem with the programs we explored in the last chapter. The MarketPlace server, the RaceTrack server, and eChat are not persistent: once you shut them down they are gone forever. You can start up a new server with the same code, but it will not return to life with the same URI it had before. As a consequence, if you restart a server, you must redistribute the capability to access that server to all the users. This is a logistical nightmare. When we restart one of these servers, it must be able to restart with the same VatID and object IDs it had before.
If E simply allowed objects to remember their own URIs, there would be extensive opportunities for object forgery in the mobile code systems described in the next chapter. To prevent such forgeries, E uses swiss bases for persistence.
Swiss Bases
(XXX need to explain swiss numbers within Walnut, either here or earlier.)
(XXX should Walnut link to general wiki pages, or only within itself?)
To claim (to the comm system) that an object is that which should be referred to by an outside sturdyRef, the swiss base for that sturdyRef must be presented. The 'swiss base' is the number which hashes to the swiss number in the sturdyRef. Knowing the swiss base allows you to compute the swiss number, but knowing the swiss number does not allow you to compute the swiss base.
Every holder of a sturdyRef knows its swiss number. Only the party which controls what the sturdyRef refers to knows its swiss base.
If you use timeMachine
to manage your persistent program, as the example below does, then makeSturdyRef
manages the swiss bases for you.
Example: Persistent eChat
(XXX write explanatory text, and review this code w.r.t. what concepts Walnut has and hasn't introduced. Was copied from persist-echat.e-awt in the distribution.)
#!/usr/bin/env rune
pragma.syntax("0.8")
# Copyright 2002 Combex, Inc. under the terms of the MIT X license
# found at http://www.opensource.org/licenses/mit-license.html ................
/**
* set up tracing; stub out all the printing for operational version
*/
def traceline(str) :void {
stderr.println(str)
}
# Ensure the user knows if he's using a clear, unencrypted connection
traceline(introducer.negotiable())
#def Title := if (introducer negotiable() == ["3DES_SDH_M"]) {
# "Secure EChat"
#} else {
# <swing:makeJOptionPane> showMessageDialog(
# null,
# "You are using DaffE, so you'll be chatting in the clear.
#To be secure, use E instead.",
# "Unencrypted DaffE Session",
# <swing:makeJOptionPane> WARNING_MESSAGE())
# "EChat"
#}
def Title := "eChat"
/**
* return the object represented by the URI
*/
def getObjectFromURI(uri) :any {
introducer.sturdyFromURI(uri).getRcvr()
}
/**
* return the friend file
*/
def findFriendFile(chatWin) :any {
def dialog := <awt:makeFileDialog>(chatWin, "Select a Friend")
dialog.show()
var path := dialog.getFile()
if (path != null) {
path := dialog.getDirectory() + path
}
<file>[path]
}
/**
* return a file to be saved
*/
def requestSaveFile(chatWin) :any {
def dialog := <awt:makeFileDialog>(chatWin,
"Save File with Your Name",
<awt:makeFileDialog>.getSAVE())
dialog.show()
var addressName := dialog.getFile()
if (addressName != null) {
addressName := dialog.getDirectory() + addressName
}
<file>[addressName]
}
/**
* method that writes out the URI for your echat system's communication
* interface
*/
def offerMyAddress(file, uri) :void {
traceline(`$file setText("$uri")`)
file.setText(uri)
}
def set1LineComponentParameters(component) :void {
component.setPreferredSize(<awt:makeDimension>(150,25))
component.setMaximumSize(<awt:makeDimension>(1000,25))
component.setAlignmentX(0.5)
}
def chatUIMaker(chatController) :any {
# Lay out the chat window, create its components
def chatWin := <swing:makeJFrame>(Title)
def chatPane := chatWin.getContentPane()
def border := <swing:makeBoxLayout>(chatPane,1)
chatPane.setLayout(border)
traceline("p1 ui made");
def windowListener {
to windowClosing(event) :void {
chatController.leave()
traceline("trying to exit")
interp.continueAtTop()
}
match _ {}
}
chatWin.addWindowListener(windowListener)
traceline("p2 ui made");
/**
* make a button that calls the chatController
*/
def newButton(labelText, verb) :any {
# def button := <swing:makeJButton>(labelText)
def button := <swing:makeJButton>(labelText)
traceline("made button")
set1LineComponentParameters(button)
def buttonListener {
to actionPerformed(event) :void {
E.call(chatController, verb, [])
}
}
button.addActionListener(buttonListener)
traceline("button being returned")
button
}
# setNameButton
def setNameButton := newButton("Set Your Name", "setMyName")
# offerChatButton
def offerChatButton := newButton("Offer Chat", "offerSelf")
offerChatButton.setEnabled(false)
# findFriendButton
def findFriendButton := newButton("Find Friend", "findFriend")
findFriendButton.setEnabled(false)
# chatScroller that holds chatTextArea
def chatScroller := <swing:makeJScrollPane>()
chatScroller.setMaximumSize(<awt:makeDimension>(2000,1000))
chatScroller.setPreferredSize(<awt:makeDimension>(250,80))
# chatTextArea
def chatTextArea := <swing:makeJTextArea>()
chatTextArea.setLineWrap(true)
chatScroller.getViewport().add(chatTextArea)
# nextMessageBox
def nextMessageBox := <swing:makeJTextField>("Type your message here",30)
set1LineComponentParameters(nextMessageBox)
chatPane.add(nextMessageBox)
traceline("p3 ui buildt");
# sendMessageButton
def sendMessageButton := newButton("Send", "send")
sendMessageButton.setEnabled(false)
chatPane.add(JPanel`$setNameButton
$offerChatButton $findFriendButton
$chatScroller.Y
$nextMessageBox
$sendMessageButton`)
chatWin.pack()
chatWin.show()
def chatUI {
to getChatWin() :any { chatWin }
to getNameButton() :any { setNameButton }
to getOfferChatButton() :any { offerChatButton }
to getFindFriendButton() :any { findFriendButton }
to getChatTextArea() :any { chatTextArea }
to getNextMessageBox() :any { nextMessageBox }
to getSendMessageButton() :any { sendMessageButton }
}
}
/**
* facet of chatController sent to other chatter with only appropriate
* messages
*/
def chatReceiverMaker(var chatController) :any {
def chatReceiver {
to receive(message) :void { chatController.receive(message) }
to receiveFriend(friend, name) :any {
chatController.receiveFriend(friend, name)
}
to friendIsLeaving() :void { chatController.friendIsLeaving() }
to revoke() :void { chatController := null }
}
}
/**
* @author Marc Stiegler
*/
def chatControllerMaker() :any {
def chatController := {
def chatUI := chatUIMaker(chatController)
def myChatReceiver := chatReceiverMaker(chatController)
var myName := null
var myFriend := null
var myFriendName := null
var myAddressFile := null
traceline("initialized chatController");
def showMessage(senderName, message) :void {
chatUI.getChatTextArea().append(
senderName +" says:\t"+ message + "\n")
}
def showDebug(message) :void {
# chatUI getChatTextArea() append("Debug: " + message + "\n")
}
def chatController {
to getChatReceiver() :near { myChatReceiver }
/**
* transmitting functions
*/
to send() :void {
def nextMsgBox := chatUI.getNextMessageBox()
def nextMessage := nextMsgBox.getText()
nextMsgBox.setText("")
traceline("next message" + nextMessage)
myFriend <- receive(nextMessage)
showMessage(myName, nextMessage)
}
to setMyName() :void {
myName := <swing:makeJOptionPane>.showInputDialog(
"Please give me your name for this chat session")
traceline(`name: $myName`)
if (myName != null) {
chatUI.getNameButton().setLabel(myName)
chatUI.getNameButton().setEnabled(false)
chatUI.getOfferChatButton().setEnabled(true)
chatUI.getFindFriendButton().setEnabled(true)
}
}
to offerSelf() :void {
myAddressFile := requestSaveFile(chatUI.getChatWin())
if (myAddressFile != null) {
def sturdyChatReceiver := makeSturdyRef(myChatReceiver)
if (timeMachine.isSaved()) {
timeMachine.save()
}
def uri := introducer.sturdyToURI(sturdyChatReceiver)
offerMyAddress(myAddressFile, uri)
}
}
to leave() :void {
if (myAddressFile != null) {
# myAddressFile delete(null)
}
myFriend <- friendIsLeaving()
chatController.disconnect("is being left")
}
to receive(message) :void {
showMessage(myFriendName, message)
}
to receiveFriend(friend, name) :any {
traceline("receiveFriend")
myFriend := friend
myFriendName := name
chatUI.getSendMessageButton().setEnabled(true)
chatUI.getOfferChatButton().setEnabled(false)
chatUI.getFindFriendButton().setEnabled(false)
chatUI.getChatTextArea().append(`$myFriendName has arrived$\n`)
Ref.whenBroken(friend, def observer(prom) :void {
chatController.disconnect("disconnected")
})
traceline("received")
myName
}
to findFriend() :void {
def file := findFriendFile(chatUI.getChatWin())
if (file != null) {
def friendURI := file.getText()
showDebug("uri " + friendURI)
def friend := getObjectFromURI(friendURI)
showDebug("obj " + friend)
when (friend <- receiveFriend(myChatReceiver, myName)) ->
done(friendName) :void {
showDebug("won against all odds")
chatController.receiveFriend(friend, friendName)
} catch problem {
showDebug("clobbered: " + problem)
chatController.disconnect("is unreachable")
}
showDebug("sent me")
}
}
to friendIsLeaving() :void {
chatController.disconnect("is leaving")
}
to disconnect(desc) :void {
chatUI.getSendMessageButton().setEnabled(false)
if (myFriendName == null) {
myFriendName := "the friend"
}
chatUI.getChatTextArea().append(
myFriendName + " " + desc + "\n")
myFriend := null
myFriendName := null
myChatReceiver.revoke()
}
}
}
}
def controller := chatControllerMaker()
def chatReceiver := controller.getChatReceiver()
def makeAtomicFile := <import:org.erights.e.extern.persist.makeAtomicFile>
def makeVatFile(filename :String) :near {
def file := <file>[filename]
makeAtomicFile(<file>[file.getParent()], file.getName())
}
timeMachine.addExit(chatReceiver, "chatReceiver", true)
if (interp.getArgs() =~ [`-save`, filename]) {
traceline(`first incarnation as $filename`)
timeMachine.new()
def vatFile := makeVatFile(filename)
timeMachine.createAs(vatFile)
} else if (interp.getArgs() =~ [`-restore`, filename]) {
traceline(`reincarnation from $filename`)
def vatFile := makeVatFile(filename)
timeMachine.revive(vatFile)
} else {
traceline("New unnamed incarnation")
timeMachine.new()
}
introducer.onTheAir()
traceline(introducer)
traceline(`vatID: ${introducer.getVatID()}`)
traceline(`netConfig: ${introducer.getNetConfig()}`)
traceline(interp.getArgs())
interp.blockAtTop()
Next Section: Secure Mobile Code