Business Functions

The following is a matrix of all the possible flows of funds between a self-custodial wallet, lockbox and balance:

From/To
Wallet
Lockbox
Balance

Wallet

retrieve() from forwarder

send()

deposit()

Lockbox

reclaim() if sent from wallet

N/A

claim() reclaim() (if sent from balance)

Balance

withdraw()

withdrawAndSend()

withdrawAndDeposit()

All but the Lockbox-to-Lockbox combination are relevant and included in this design.

1. createBalance()

Create a new zero-balance for new users.

To avoid error, call isCommitmentExist() as sanity check to ensure balanceCommitment does NOT already exist.

Parameters:

  • salt = hash(email + password)

  • balanceCommitment (=hash(hash(email + password + currentSalt)))

Behaviors:

  • If salt exists, validate balance exists, and commitment chain is well-defined. The function is idempotent.

  • Otherwise, validates balance does not exist, create new identitySalt (=hash(email + password)) as currentSalt.

  • Create zero balance, set updateTime as current time.

  • In one embodiment, createBalance() is supported by 2FA server, which requires the new user to first click on a link sent by the server containing a secret, and thus verify ownership of the email account.

2. send()

Send funds from a sender's wallet into a temporary lockbox.

To avoid error, call isLockboxAvailable() as sanity check to ensure commitment not in use.

Parameters:

  • amount

  • duration (timeout duration. Recipient can claim the amount before timeout, after which the Sender can reclaim it back)

  • commitment (double-hashed claim proof =hash(hash(hash(email + passphrase) + lockboxSalt))

Behaviors:

  • Validate:

    • Amount is not too low (waste gas)

    • Duration is not too low (frivolous)

    • Commitment is not zero

    • Lockbox does not always exist under the same commitment (duplicate)

  • Records lockbox sender, amount, createTime and unlockTime

  • Status initialized to Open

  • In one embodiment, to support 2FA and non-repudiation, commitment was computed by sender as follows:

    • Sender sets recipient email, passphrase and optionally passphrase hint

    • Sender client submits email and passphrase-hint to layer-2 server

    • Layer-2 server creates random value R, then

      • Send lockboxSalt (=hash(R)) to sender

      • Stores the pending notification parameters (email, hint, R)

    • Sender client computes hash(hash(email + passphrase + lockboxSalt)), and call send() directly to blockchain

    • Sender requests server to send email to recipient, which includes URL with email, hint and R

    • When later the recipient uses the URL, the server will note the knowledge of R in the URL. That forms the basis of non-repudiation: the sender cannot claim his own deposit, then accuse the recipient of having done so, since the claim would have lacked the knowledge of R

  • Under another embodiment, where 2FA and non-repudiation are not required or undesirable, commitment was computed by sender as follows:

    • Sender sets recipient email, passphrase and optionally passphrase hint

    • Sender client creates random value R, and computes lockboxSalt (=hash(R))

    • Sender client computes hash(hash(email + passphrase + lockboxSalt)), and call send() directly to blockchain

    • Sender client creates URL (or its QR code representation) including email, hint and lockboxSalt. Note that R is missing in the URL

    • Sender sends or presents URL (or its QR code representation) directly to recipient

    • Since R does not exist in the URL, there is no non-repudiation

3. isLockboxAvailable(), getLockbox()

  • isLockboxAvailable() returns true if no lockbox is found under the commitment.

  • getLockbox() retrieves the details of a lockbox based on the provided lockbox commitment.

Parameters:

  • commitment: the unique commitment of the lockbox. This hash is derived from a user’s email, passphrase, and an additional lockbox salt, and is used to uniquely identify each lockbox.

Behaviors:

  • Validates:

    • lockbox commitment is correct, by checking lockboxes[hash(proof)].createTime != 0

  • If the lockbox exists, return all the lockbox details

  • If it does not exist, the function will revert with an error

4. claim()

Recipient moves funds from a lockbox into balance.

For new user, they must first call isCommitmentExist() to ensure balanceCommitment does NOT already exist.

For existing users, they must first call getBalance() to ensure it is valid.

Parameters:

  • proof = hash(email + passphrase + lockboxSalt)

  • salt = hash(email + password)

  • balanceCommitment (=hash(hash(email + password + currentSalt)))

Behaviors:

  • Validates:

    • lockbox commitment is correct, by checking lockboxes[hash(proof)].createTime != 0

    • lockbox is still Open

    • lockbox is not expired

    • amount is not too low (sanity check)

  • If salt exists, validate balance exists. CurrentSalt can be obtained by calling getCurrentSalt(salt)

  • Otherwise, validates balance does not exist, create new identitySalt (=hash(email + password)) as currentSalt

  • Increase balance with box amount, set updateTime as current time, empty box, and set to claimed

  • Note that lockboxSalt was either given by the sender directly in the URL (under no-2FA scenario), or computed by the recipient (lockboxSalt = hash(R)) based on R given in the URL (under the 2FA scenario)

  • In one embodiment, the recipient can elect to store their password in local device keystore, secured by biometrics or system passwords

5. deposit(), isCommitmntExist()

  • isCommitmentExist() verifies that it is an existing commitment on chain, current or previous.

  • deposit() funds to an existing pseudonymous balance identified by a commitment.

Parameters:

  • amount

  • commitment: The (possibly outdated) balance commitment. The contract will resolve to the current commitment using the commitment chain.

Behaviours:

  • Validates:

    • amount must be at least MIN_AMOUNT

    • The resolved commitment must correspond to an existing balance

  • Resolve to the current commitment on the commitment chain

  • Credits amount to the resolved balance, and set updateTime as current time

  • Reverts if no valid balance is found

6. getBalance(), retrieve()

  • getBalance() looks up their existing balance. Layer-2 server user flow can call this to provide visual confirmation to the user before and after their new funds are claimed.

  • retrieve() will check the balance on forwarder, and instruct it to release any funds held.

Parameters:

  • commitment (=hash(hash(email + passphrase + currentIdentitySalt)))

Behavior:

  • returns the current balance

  • Checks if Forwarder for the commitment exists. If yes, it signals it to release all its held funds, so the balance is updated

7. withdraw()

Withdraw funds from a balance commitment to a real wallet (on-chain address).

Caller must first call isCommitmentExist() as sanity check to ensure nextCommitment does NOT already exist.

Parameters:

  • proof = hash(email + password + currentSalt)

  • salt = hash(email + nextPassword)

  • amount

  • nextCommitment = hash(hash(email + nextPassword + nextSalt))

  • recipient = address of recipient

Behaviors:

  • Validates

    • proof, by comparing hash(proof) to an existing balance’s commitment

    • amount is at least MIN_AMOUNT and no more than available balance

    • salt exists

    • nextCommitment does not exist on commitment chain nor balances, and it is not equal to the current one

    • recipient address is not address(0) (burns)

  • Increment salt iteration

  • Shift reduced balance to nextCommitment, and set updateTime as current time

  • Update commitment chain for the withdrawer

  • Withdraw to recipient address

8. withdrawAndSend()

Withdraw funds from a balance commitment into a lockbox, allowing the recipient to claim it pseudonymously.

Caller must first call isCommitmentExist() as sanity check to ensure nextCommitment does NOT already exist.

And call isLockboxAvailable() as sanity check to ensure commitment is not in use.

Parameters:

  • proof = hash(email + password + currentSalt)

  • salt = hash(email + nextPassword)

  • amount

  • nextCommitment = hash(hash(email + nextPassword + nextSalt))

  • duration = number of seconds the lockbox remains claimable

  • commitment = double-hashed lockbox key (used by the recipient to claim)

Behavior:

  • Validates

    • proof, by comparing hash(proof) to an existing balance’s commitment

    • amount is at least MIN_AMOUNT and no more than available balance

    • salt exists

    • nextCommitment does not exist on commitment chain nor balances, and it is not equal to the current one

    • duration is at least MIN_DURATION

  • Increment salt iteration

  • Shift reduced balance to nextCommitment, and set updateTime as current time

  • Update commitment chain for the withdrawer

  • Creates a new lockbox with:

    • amount

    • sender = address(0)

    • senderCommitment = nextCommitment

    • unlockTime = now + duration

    • status = Open

9. withdrawAndDeposit()

Withdraw funds from a balance commitment to another pseudonymous balance (rebind).

Caller must first call isCommitmentExist() as sanity check to ensure nextCommitment does NOT already exist.

And call isCommitmentExist() to ensure recipient’s commitment exists.

Parameters:

  • proof = hash(email + password + currentSalt)

  • salt = hash(email + nextPassword)

  • amount

  • nextCommitment = hash(hash(email + nextPassword + nextSalt))

  • commitment of recipient (along the chain of commitments) to receive the deposit

Behavior:

  • Validates

    • proof, by comparing hash(proof) to an existing balance’s commitment

    • amount is at least MIN_AMOUNT and no more than available balance

    • salt exists

    • nextCommitment does not exist on commitment chain nor balances, and it is not equal to the current one

  • Increment salt iteration

  • Shift reduced balance to nextCommitment, and set updateTime as current time

  • Update commitment chain for the withdrawer

  • Resolve to current commitment using commitment chain for the deposit

  • Increases the balance of resolved commitment by amount

10. changPassword()

Recipient changes password for an existing balance.

Caller must first call isCommitmentExist() as sanity check to ensure nextCommitment does NOT already exist.

Parameters:

  • proof = hash(email + password + currentSalt)

  • nextSalt = hash(email + nextPassword)

  • nextCommitment = hash(hash(email + nextPassword + nextSalt))

Behaviors:

  • Validates

    • proof, by comparing hash(proof) to an existing balance’s commitment

    • nextSalt does not exist (i.e. passwords cannot be reused)

    • nextCommitment does not exist on commitment chain nor balances

  • Save new salt

  • Shift balance

  • Update commitment chain

11. reclaim()

Reclaim funds from a lockbox after it has passed its unlock time. It can transfer the funds back to the sender’s wallet if it was sent by send(), or to the sender’s balance if it was sent by withdrawAndSend().

To avoid error, first call getLockbox() as sanity check to ensure lockbox exists, and verify its expiry status before electing to reclaim it.

Parameters:

  • commitment (double-hashed claim proof =hash(hash(hash(email + passphrase) + lockboxSalt))

Behaviors:

  • Validates

    • commitment points to a lockbox

    • status is still Open

    • unlockTime has expired

    • amount is not too low (sanity check)

  • Sends funds back to sender’s wallet, or to balance under senderCommitment, as recorded at the time when the Lockbox was created

  • Lockbox amount set to 0, and status to Reclaimed

  • Note that knowledge of proof is not required. As long as the sender (or Layer-2 server) has the commitment, it can trigger the reclaim()

Last updated