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:
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!
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 with
G). 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
Here’s how things look at the beginning:
Balance: 500 XLM
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 now
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
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: GESCROW
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
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:
Balance: 400 XLM (100 transferred to GESCROW)
Balance: 1600 XLM
Balance: 100 XLM (100 from GCUSTOMER)
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
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: GCUSTOMER
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
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 (
GESCROW) it will require three signatures:
GWORKERto provide 1 of 2 necessary weights to merge
GCUSTOMERsince they are the source account (which means they are paying all fees) and are 1 of 2 necessary weights to merge
GVANITYbecause we’re altering the thresholds and master key weights of
Since the Worker knows the private keys for
GVANITY it can provide these signatures and write them as data entries on the
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:
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_proofis a valid signature. This is how we know that
GVANITYwas really generated.
txTransfer. The Customer and Worker both independently build this transaction. All that needs to be exchanged are the signatures.
- Add a signature from
- Add the
_sigVsignatures that were read from the
txTransferto 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
For example, to pay 50 XLM to
GMERCHANT, the following transaction would be generated:
Transaction Name: txExamplePayment
Submitted By: GCUSTOMER
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
GCUSTOMER can provide a weight of 1 with their signature which is enough to perform any operation.
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