Distributed Trustless Workers with Stellar
This article describes something you can do with cryptocurrency besides speculating on the price: pay a distributed network of untrusted workers to create something for you.
Read on for an in-depth look at how Stellar’s advanced features can be used to create a “smart contract” between a customer and an anonymous and untrusted worker.
Finding something to create
Bitcoin and Stellar are both built around the concept of an “address” that is used to send payments. An example Stellar address is: GABFWOKKMDRIDRTUBI6M5SNMABX6XGRPYBYMJ24IIFCDYYZRCKTSKCXZ
Since that series of random letters and numbers isn’t very user friendly, it’s nicer to have a recognizable address that starts or ends with an english word. For example, if the last four letters were ZULU
then it would be much easier to recognize this address and verify a payment was going to the right place.
Custom addresses like this are known as “vanity addresses” and need to be generated by randomly creating addresses until one is found. Generating a series of four characters would go quickly, but generating ZULUCRYPTO
would take much longer. This is a perfect problem for a distributed group of workers!
Process Overview
There are two actors involved in this transaction:
- The Customer who wants a vanity address generated. I’ll abbreviate their Stellar account as
GCUSTOMER
(Stellar addresses always start withG
). They start with 500 XLM and want to pay 100 XLM for a vanity address. - The Worker who generates the vanity address:
GWORKER
. The Worker is willing to accept 100 XLM to generate a vanity address.
I’ll refer to the vanity address that gets generated as GVANITY
.
Here’s how things look at the beginning:
Account: GCUSTOMER
Balance: 500 XLMAccount: GWORKER
Balance: 1600 XLM
Customer Advertises their Request
In order to get work started, the Customer has to solve a few problems:
- How to advertise to the network that there is work to be done?
- How to prove to a Worker that the worker will get paid?
Advertising the request
First, the Customer has to figure out a way to advertise what vanity keypair they want. This is done by setting a Data Entry on an account. Each data entry is a key/value pair, so as long as the Customer and Worker can agree on a standard, this is a way to broadcast something to the entire network.
In this case, we’ll set the key request:generateVanityAddress
to value G*ZULU
. Now, the Worker can scan the blockchain for any accounts with the request:generateVanityAddress
data and then examine the value to see what vanity address is requested. In this case, it’s anything that starts with “G” and ends in “ZULU”.
Proving that a Worker will get paid
This can be solved through the use of an Escrow account (that the Customer creates) which we’ll refer to as GESCROW
. In order for an escrow account to be effective, the Worker needs to know that the Customer has the funds available to pay them and that the Worker will be able to access those funds when the vanity address has been generated.
The Customer also needs some way to reclaim their funds if a Worker never completes their task.
We’ll go in reverse and come up with a way for the Customer to retrieve their funds by creating a pre-authorized transaction that’s not valid until 30 days have passed:
Transaction Name: txReclaimAccount
Submitted By: GESCROW
Timebounds: Valid starting 30 days from nowOperations:
1. Merge GESCROW into GCUSTOMER
Timebounds are a Stellar feature that allows you to create a transaction that’s only valid during a specific time range. For more details on transactions, see: https://www.stellar.org/developers/guides/concepts/transactions.html
The Merge operation destroys an account and sends all funds to a destination account. The operation above will destroy GESCROW
freeing up its resources and sending the balance back to GCUSTOMER
.
The customer now saves this transaction somewhere and can submit it to the network after 30 days have passed.
Now it’s time to prove to the Worker that the Customer no longer has access to the escrow account for these 30 days. To solve this problem, we use Stellar’s multisignature features.
As the Customer, we immediately submit this transaction to the network:
Transaction Name: txShareAccount
Submitted By: GESCROWOperations:
1. Add GWORKER as a signer with weight 1
2. Add GCUSTOMER as a signer with weight 1
3. Require a weight of 2 to perform any actions on the account
4. Set the weight of the master key (GESCROW) to 0
After this transaction is submitted, a weight of 2 is required to do anything with this account. Since the GESCROW
master key now has a weight of 0, the only way for anything to happen is if GCUSTOMER
and GWORKER
agree on a transaction and both sign it.
The one exception to this is the pre-authorized txReclaimAccount
transaction, but since it’s not valid for 30 days, the Worker is guaranteed 30 days to work on generating the vanity address.
At this point, we have the following accounts:
Account: GCUSTOMER
Balance: 400 XLM (100 transferred to GESCROW)Account: GWORKER
Balance: 1600 XLMAccount: GESCROW
Balance: 100 XLM (100 from GCUSTOMER)
Signers:
1. GCUSTOMER (weight 1)
2. GWORKER (weight 1)
3. Master key can no longer sign
4. Pre-authorized transaction (valid starting 30 days from now)
To keep things organized, GESCROW
is also the account that the request:generateVanityAddress
data entry mentioned above is attached to.
Worker begins work
We’ll switch over to the Worker’s perspective now. By scanning the blockchain, we’ve discovered that someone has requested an address that ends in “ZULU”.
Since this request is attached to the Escrow account, we can see that the price is reasonable (100 XLM) and that the escrow account is locked and cannot be reclaimed by the Customer while we’re working on it.
The actual algorithm for generating a vanity address involves repeatedly generating random keys until we find one that matches the Customer’s desired pattern.
At some point, we’ll find a matching address and then we have to begin the process of communicating it to the Customer.
Worker proves that they found a keypair
After finding a keypair, the Worker’s challenge is to prove to the Customer that they’ve generated it. There also needs to be a way for the Customer to pay the Worker by transferring the escrow account.
This process will again rely on account data entries. In this case, they will be attached to the Worker account.
First: set a data entry with key:value GCUSTOMER_result:GVANITY
. This allows the Customer to examine the data entries on GWORKER
to check if the Worker is done.
Then, we need a way to prove to the Customer that the Worker controls the private key for GVANITY
without revealing what that private key is. This process should sound familiar since that’s how cryptocurrencies work! When you’re submitting a transaction to the network you’re using your secret key to sign a transaction. In this case, we’ll sign a text string and write the signature to our account as the following key:value pair: GCUSTOMER_proof:<ed25519 signature>
. The text being signed doesn’t matter as long as GCUSTOMER
and GWORKER
both know what it is.
The final thing the Worker needs to take care of is generating signatures for the transfer transaction.
The transfer transaction
This transaction is the critical part of the process and allows the Customer and Worker to exchange payment and data in a single operation. Due to the properties of the Stellar Consensus Protocol, all operations in a transaction will either succeed or fail. There’s no way for the transaction to be partially completed.
This transaction will look like the following:
Transaction Name: txTransfer
Submitted By: GCUSTOMER (see the next step)
Source Account: GCUSTOMEROperations:
1. GVANITY: Add GCUSTOMER as a signer with weight 1
2. GVANITY: Set all thresholds to 1
3. GVANITY: Set master key weight to 0
4. GESCROW: Merge to GWORKER
The first three operations take care of giving control of GVANITY
to GCUSTOMER
. After the above transaction runs, the secret key to GVANITY
(only known by GWORKER
) will no longer be able to perform any operations on the account since its weight will be 0 and all operations have a threshold of 1.
The last operation is how GWORKER
receives payment: GESCROW
is merged to GWORKER
which transfers all XLM in the account.
Since this transaction involves three accounts (GCUSTOMER
, GVANITY
, and GESCROW
) it will require three signatures:
GWORKER
to provide 1 of 2 necessary weights to mergeGESCROW
GCUSTOMER
since they are the source account (which means they are paying all fees) and are 1 of 2 necessary weights to mergeGESCROW
GVANITY
because we’re altering the thresholds and master key weights ofGVANITY
Since the Worker knows the private keys for GWORKER
and GVANITY
it can provide these signatures and write them as data entries on the GWORKER
account.
Note that the Worker is not submitting the above transaction! They only generate the signatures for it. It will be the Customer’s responsibility to provide the final signature and submit to the network.
Customer verifies vanity keypair and submits transfer transaction
The Customer checks the data entries on GWORKER
and sees the following:
Account: GWORKER
Data Entries:
GCUSTOMER_result: GVANITY
GCUSTOMER_proof: <ed25519 signature>
GCUSTOMER_sigW: <worker stellar signature for txTransfer>
GCUSTOMER_sigV: <vanity keypair stellar signature for txTransfer>
The Customer now takes the following steps:
- Verify that the data in
GCUSTOMER_proof
is a valid signature. This is how we know thatGVANITY
was really generated. - Build
txTransfer
. The Customer and Worker both independently build this transaction. All that needs to be exchanged are the signatures. - Add a signature from
GCUSTOMER
totxTransfer
- Add the
_sigW
and_sigV
signatures that were read from theGWORKER
data entries - Submit
txTransfer
to the Stellar network now that the Customer has all of the necessary signatures
As soon as txTransfer
is submitted and confirmed then the process is complete!
Customer uses vanity address
The Customer is now in control of the vanity address, but you’ll note they never received the secret key.
Instead, Stellar’s multisig features are used to allow GCUSTOMER
to sign for GVANITY
.
For example, to pay 50 XLM toGMERCHANT
, the following transaction would be generated:
Transaction Name: txExamplePayment
Submitted By: GCUSTOMEROperations:
1. From GVANITY: Pay 50 XLM to GMERCHANT
GCUSTOMER
would then sign this transaction with their own secret key. Even though the payment is coming from GVANITY
, GCUSTOMER
can provide a weight of 1 with their signature which is enough to perform any operation.
Real-world Implementation
A working implementation of this would require that Customers and Workers agreed on some standards. It would also require additional data entries for better communication between the two parties.
Source code implementing a test version of this smart contract is available at: https://github.com/zulucrypto/stellar-smart-contracts/blob/master/distributed-worker/README.md