Business Functions
The following is a matrix of all the possible flows of funds between a self-custodial wallet, lockbox and 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.
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
updateTimeas 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.
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 callsend()directly to blockchainSender 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 blockchainSender 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.
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 != 0lockbox 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 currentSaltIncrease 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
updateTimeas current timeReverts 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).
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 commitmentamount 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 setupdateTimeas current timeUpdate 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.
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 commitmentamount 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 setupdateTimeas current timeUpdate 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).
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.
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 commitmentnextSaltdoes not exist (i.e. passwords cannot be reused)nextCommitmentdoes 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().
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
