[E-Lang] ERTP-aware MintMaker

Tyler Close tclose@oilspace.com
Wed, 14 Feb 2001 18:38:38 -0000


I've been meaning to bring up the topic of the ERTP for some time now.
I need some pretty major changes to it.

The ERTP, as it is currently specified, has at least the following
problems:
-Assay objects are not pass-by-copy. This creates some real headaches
if you are writing a remote smart contract. For example, just sorting
bids and asks by amount becomes a real problem.
-The authority to create new Purses is not restricted. In reality, the
backer of a particular brand of eright may not be willing to let just
anyone hold its erights. The current ERTP does not provide for this.
Since a Purse is also an object with mutable state, it is very
expensive to just let anyone create as many as they like. This is a
sweet target for a DOS attack. The sturdyVouch() method makes this
worse.
-The API does not provide a straight forward way of implementing a
version that keeps an audit trail of all transactions. Since the API
requires the ability to create anonymously owned Purses, a
non-anonymous implementation is tricky.
-The authority to perform transfers is not protected. In reality, some
brands of erights may be created for the sole purpose of providing
work material for money making smart contracts. Hosting of the actual
erights may be a "loss leader". In this case, the authority to perform
eright transfers is the most valuable right, since it represents the
ability to host money making smart contracts.

Anyone one of these reasons is a major fault. Taken together, they
require a complete redesign.

I've taken a crack at this redesign and am happy with the results. I
am submitting it for review.

My proposed ERTP interface comprises 5 interfaces [ Assay, Conduit,
Purse, PurseObserver, PurseFactory ]. It's larger than the current
ERTP, but still manageable. I'll use a Java-like syntax to describe
the proposed interface.

# Represents a quantity of erights of a particular brand
# Implementations are 'transparent' (see:
http://www.erights.org/elang/same-ref.html)
interface Assay {
	to compareTo(other) :any
	to isEmpty() :boolean
	to getBrand() :any
}

# The authority to transfer erights of a particular brand.
interface Conduit {
	to getBrand() :any
	to makeEmptyAssay() :any

	# Repeatedly transfers an amount of erights.
	# Returns the total amount of transferred erights.
	to transfer(n :(int >= 0), amount, src, dst) :any

	# Transfers all erights.
	# Returns the amount of transferred erights.
	to deliver(src, dst) :any
}

# The authority to hold erights of a particular brand.
interface Purse {
	to getBrand() :any

	# Returns an Assay
	to getBalance() :any

	to observe(observer) :void
}

# Observes changes in the contents of a Purse.
interface PurseObserver {
	to deposited(amount) :void
	to withdrawn(amount) :void
}

# The authority to create new Purses.
interface PurseFactory {
	to getBrand() :any
	to makePurse() :any
}

To further clarify this API, I have also taken a stab at implementing
it in E. Commodity defines a fungible, non-divisible type of eright.
I've used the Hydro collection library, so it doesn't work in E as is.
It should be close enough to working for people to understand the
intent.

def Assay(brand, value) :any {
    def self {
        to openState :any { meta scope }
        to openSource :any { meta sourceTree(self) }
        to printOn(out) :void { out print(`<Assay: $value $brand>`) }
        to compareTo(other) :any { value compareTo(other getValue) }
        to isEmpty :boolean { value == 0 }
        to getBrand() :any { brand }
        to getValue() :any { value }
    }
}

def Commodity(label) :any {
    def [ sealer, unsealer ] := BrandMaker pair(label)
    def Purse(mint :boolean) {
        var balance :int := 0
        var observers := [  ] toFIFO
        def private {
            to getValue() :int { balance }
            to withdraw(amount :(int >= 0)) :void {
                if(amount > balance && !mint) {
                    throw(InsufficientErights(`$amount > $balance`))
                }
                balance -= amount
                for x in observers {
                    x <- withdrawn(Assay(sealer brand, amount))
                }
            }
            to deposit(amount :(int >= 0)) :void {
                balance += amount
                for x in observers {
                    x <- deposited(Assay(sealer brand, amount))
                }
            }
        }
        def public {
            to getBalance() :any { Assay(sealer brand, balance) }
            to getBrand() :any { sealer brand }
            to observe(observer) :void { observers += observer }
            to pack() :any { sealer seal(private) }
        }
    }
    def PurseFactory {
        to getBrand() :any { sealer brand }
        to makePurse() :any {
            Purse(false)
        }
    }
    def Conduit {
        to getBrand() :any { unsealer brand }
        to makeEmptyAssay() :any { Assay(unsealer brand, 0) }
        to deliver(src, dst) :any {
            def src_balance := unsealer unseal(src pack)
            def dst_balance := unsealer unseal(dst pack)
            def delta := src_balance getValue
            src_balance withdraw(delta)
            dst_balance deposit(delta)
            Assay(unsealer brand, delta)
        }
        to transfer(n :(int >= 0), amount, src, dst) :any {
            assert(amount getBrand == unsealer brand) # Just for
debugging.
            def src_balance := unsealer unseal(src pack)
            def dst_balance := unsealer unseal(dst pack)
            def delta := n * amount getValue
            src_balance withdraw(delta)
            dst_balance deposit(delta)
            Assay(unsealer brand, delta)
        }
    }
    [ PurseFactory, Purse(true), Conduit ]
}

Tyler Close, Founder Waterken Inc.
tyler@waterken.com
A35E 0621 44AD B616 DE29  F8DF 7B4C E859 71AB 47C5