# This is a modern E version of the code from # http://www.erights.org/history/original-e/devil.html # WARNING: translated by a beginner at crypto protocols and # distributed E programs, and it's a liberal translation -- # I've felt free to change the detailed interface. The # protocols should still be the same. # Given two integers, return an integer in 1..6 depending on both. # We must have combine(a, b) == combine(b, a). def combine(a :int, b :int) :int { return ((a ^ b) % 6) + 1 } # Dicing with the Devil: Act I def makeDieRoller(other) :any { def x := entropy.nextSwiss() other <- roll(x) def [rollVow, resolver] := Ref.promise() def dieRoller { to roll(otherX :int) :void { resolver.resolve(combine(otherX, x)) } to getRoll() :any { return rollVow } } return dieRoller } def alice def bob := makeDieRoller(alice) bind alice := makeDieRoller(bob) when (alice <- getRoll()) -> done(roll) { println(roll) } catch problem { println("Oops: " + problem) } # Dicing with the Devil: Act II def makeAlice() :any { def [resultVow, resultResolver] := Ref.promise() def alice { to first(bob) :void { def x := entropy.nextSwiss() def [xVow, xResolver] := Ref.promise() when (bob <- second(x.cryptoHash(), xVow)) -> done(bobX) { xResolver.resolve(x) resultResolver.resolve(combine(x, bobX)) } catch problem { resultResolver.smash(problem) } } to getRoll() :any { resultVow } } return alice } def makeBob() :any { def [resultVow, resultResolver] := Ref.promise() def bob { to second(aliceHash :int, aliceRcvr) :int { def x := entropy.nextSwiss() when (aliceRcvr) -> done(aliceX :int) { if (aliceX.cryptoHash() != aliceHash) { throw("There was cheating") } resultResolver.resolve(combine(aliceX, x)) } catch problem { resultResolver.smash(problem) } return x } to getRoll() :any { resultVow } } return bob } def alice := makeAlice() def bob := makeBob() alice <- first(bob) when (alice <- getRoll()) -> done(roll) { println(roll) } catch prob { println("Oops: " + prob) } # Dicing with the Devil: Act III # Besides the fancier protocol, this time we factor the die-rolling logic # out from the general-purpose bit-commitment. def hash2(a :int, b :int) :int { return (a ^ b).cryptoHash() } def makeBitCommitter(other) :any { # TODO: I think we could use def/bind to make this more concise. def [bitsVow, bitsResolver] := Ref.promise() def [hashVow, hashResolver] := Ref.promise() def [xVow, xResolver] := Ref.promise() def [rVow, rResolver] := Ref.promise() other <- start(hashVow, xVow, rVow) def bitCommitter { to start(otherHashRcvr, otherXRcvr, otherRRcvr) :void { def x := entropy.nextSwiss() def r := entropy.nextSwiss() rResolver <- resolve(r) when (otherRRcvr) -> done(otherR :int) { hashResolver <- resolve(hash2(x, r)) when (otherHashRcvr) -> done2(otherHash :int) { xResolver <- resolve(x) when (otherXRcvr) -> done3(otherX :int) { if (hash2(otherX, otherR) != otherHash) { throw("There was cheating") } bitsResolver.resolve(x ^ otherX) } catch problem { # I have a vague memory that a throw here will # propagate to the enclosing when-catch, but I'm # not sure that's right. Check and fix later. throw(problem) } } catch problem { throw(problem) } } catch problem { bitsResolver.smash(problem) } } to getBits() :int { return bitsVow } } return bitComitter } def alice def bob := makeBitCommitter(alice) bind alice := makeBitCommitter(bob) # Roll the die using the bits committed to. def roll(bits :int) :int { return 1 + bits % 6 } when (alice <- getBits()) -> done(bits) { println(roll(bits)) } catch problem { println("Oops: " + problem) }