Payments

This document describes a probabilistic micropayment mechanism used by subscribers to pay relay nodes.

Operation

Escrow contract

Storage:

mapping(address => uint) lockedBalances
mapping(id => (sender, timestamp, amount)) unlockRequests
mapping(address => uint) unlockedBalances
func addEscrow(amount):
    lockedBalances[sender] += amount
    emit BalanceChanged(sender)

func unlock(amount):
    unlockRequests[hash(sender, now, amount)] = (sender, now, amount)
    return hash(sender, now, amount)

func sealUnlockRequest(id):
    if unlockRequests[id].timestamp + 86400 > now:
        return
    lockedBalances[sender] -= unlockRequests[id].amount
    unlockedBalances[sender] += unlockRequests[id].amount

func withdraw(amount):
    require(amount <= unlockedBalances[sender])
    unlockedBalances[sender] -= amount
    sender.transfer(amount)

func payForWitness(witness, amount):
    if(!witness.isWinning()) return
    for (relayer, relayerFraction) in witness:
        lockedBalances[sender] -= relayerFraction*amount
        unlockedBalances[relayer] += relayerFraction*amount

Locking funds in escrow

Users lock funds in escrow as a guarantee on payouts to winning micropayments.

Witness format

nextWitness = prevWitness | nextRelayerPubKey | relayerFraction | signature

Witness creation

Every relay node adds themselves to the witness before propagating it.

func bytes[] createWitness(bytes[] prevWitness, bytes[] nextRelayerPubKey, bytes[] relayerFraction) {
    witness = prevWitness.append(nextRelayerPubKey).append(relayerFraction)
    witness = witness.append(ecdsa.Sign(witness, self.privKey))
    return witness
}

Making a payment

The user signs the final witness with his own key and sends it to the parent node. The parent node checks if the payment is a winning micropayment and if yes, submits it to the escrow contract.

func makePayment(witness) {
    signedWitness = ecdsa.sign(witness, amount, self.privKey)
    witness.lastPeer.send(signedWitness)
}

func onNewPayment(payment) {
    if payment.isWinning() {
        EscrowContract::payForWitness(payment.witness, payment.amount)
    }
}