Two party contracts

Contracts as Escrow

A powerful use case for smart contracts is trustless decentralized escrow – where contracts act as a third party to enable transfers between parties.

An introduction to two party contracts – A Decentralised Ebay

In this tutorial we will discuss a few new ideas, namely calculating hashes in contracts and simple verification of activities outside the blockchain.

Our tutorial starts with a scenario: Alice has a guitar for sale and Bob is looking to buy a guitar – right now Alice will likely list her guitar on ebay and bob will see it, bid for it and if he wins – send the payment along with his delivery address, and Alice will make good on delivery.

contract decentralisedAuction{
	struct auction {
		uint deadline;
		uint highestBid;
		address highestBidder;
		address recipient;
	}
	mapping(uint => auction) Auctions;
	uint numAuctions;

	function startAuction(uint timeLimit) returns (uint auctionID){
		auctionID = numAuctions++;
		Auctions[auctionID].deadline = block.number + timeLimit;
		Auctions[auctionID].recipient = msg.sender;
	}
	function bid(uint id) returns (address highestBidder){
		auction a = Auctions[id];
		if (a.highestBid + 1*10^18 > msg.value || a.deadline > block.number) {
			msg.sender.send(msg.value);
			return a.highestBidder;
		}
		a.highestBidder.send(a.highestBid);
		a.highestBidder = msg.sender;
		a.highestBid = msg.value;
		return msg.sender;
	}
	function endAuction(uint id) returns (address highestBidder){
		auction a = Auctions[id];
		if (block.number >= a.deadline) {
			a.recipient.send(a.highestBid);
			a.highestBid = 0;
			a.highestBidder =0;
			a.deadline = 0;
			a.recipient = 0;
		}
	}
}

This should look familiar to anyone who has completed the crowdfund contract – the contract is built to handle ether payments and deal with timelimits for the purpose of an auction.

This is the simplest case – bidders send the maximum amount they wish to bid to the contract and once the time limit is up the seller can collect their winnings. However, In the case of Alice and Bob we have not included any protection for Bob to ensure that Alice actually delivers the Guitar. For this to happen the contract will need to act as an escrow account holding the ether as a third party until one of two things happen – Alice delivers the Guitar or a timelimit runs out.

We will amend our contract so that when bidding Bob must also send the hash of a random number known only to him. The contract will, once the auction ends, hold the ether until it receives a transaction from someone (presumably Alice or her Proxy) sends it the correct random number – or pre-image – At which point it sends the Ether to Alice. If the contract does not receive the pre-image within the alloted time the contract is voided and the ether is returned to Bob.

How this might work in the real world is that Alice might hire a courier firm to deliver to Bob the Guitar – under instructions to only hand over the Guitar once Bob has given them the pre-image which fulfils SHA256(key) = HASH. Once they have this key they may send a transaction to the blockchain themselves (or have Alice do it), to release the ether.

The new contract is here:

contract decentralisedAuction{
	struct auction {
		uint deadline;
		uint highestBid;
		address highestBidder;
		uint bidHash;
		address recipient;
	}
	mapping(uint => auction) Auctions;
	uint numAuctions;

	function startAuction(uint timeLimit) returns (uint auctionID){
		auctionID = numAuctions++;
		Auctions[auctionID].deadline = block.number + timeLimit;
		Auctions[auctionID].recipient = msg.sender;
	}
	function bid(uint id, uint biddersHash) returns (address highestBidder){
		auction a = Auctions[id];
		if (a.highestBid + 1*10^18 > msg.value || a.deadline > block.number) {
			msg.sender.send(msg.value);
			return a.highestBidder;
		}
		a.highestBidder.send(a.highestBid);
		a.highestBidder = msg.sender;
		a.highestBid = msg.value;
		a.bidHash  = biddersHash;
		return msg.sender;
	}
	function endAuction(uint id, uint key) returns (address highestBidder){
		auction a = Auctions[id];
		if (block.number >= a.deadline && sha3(key) == a.bidHash) {
			a.recipient.send(a.highestBid);
			clean(id)
		}
	}
	function clean(uint id) private{
		auction a = Auctions[id];
		a.highestBid = 0;
		a.highestBidder =0;
		a.deadline = 0;
		a.recipient = 0;
		a.bidHash = 0;
	}
}

This concept of waiting for an event to occur before revealing a secret key is a powerful one – we will learn more about this in later tutorials. It is possible to build a similar contract where we use a message from Bob to release the funds in escrow to Alice – however this has the disadvantage of needing Bob to send the transaction.

Alice knows that she wants to courier the Guitar to the eventual winner so she adds a third party to the contract:

contract decentralisedAuction{
	struct auction {
		uint deadline;
		uint highestBid;
		address highestBidder;
		uint bidHash;
		address recipient;
		address thirdParty;
		uint thirdPartyFee;
	}
	mapping(uint => auction) auctions;
	uint numAuctions;

	function startAuction(uint timeLimit, address thirdParty, uint thirdPartyFee) returns (uint auctionID){
		uint ID = numAuctions++;
		auction a = auctions[ID];
		a.deadline = block.number + timeLimit;
		a.recipient = msg.sender;
		a.thirdParty = thirdParty;
		a.thirdPartyFee = thirdPartyFee;
	}
	function bid(uint id, uint biddersHash) returns (address highestBidder){
		auction a = auctions[id];
		if (a.highestBid + 1*10^18 > msg.value || a.deadline > block.number) {
			msg.sender.send(msg.value);
			return a.highestBidder;
		}
		a.highestBidder.send(a.highestBid);
		a.highestBidder = msg.sender;
		a.highestBid = msg.value;
		a.bidHash  = biddersHash;
		return msg.sender;
	}
	function endAuction(uint id, uint key) returns (address highestBidder){
		auction a = auctions[id];
		if (block.number >= a.deadline && sha3(key) == a.bidHash) {
			a.recipient.send(a.highestBid-a.thirdPartyFee);
			a.thirdParty.send(a.thirdPartyFee);
			clean(id)
		}
	}
	function clean(uint id) private{
		auction a = Auctions[id];
		a.highestBid = 0;
		a.highestBidder =0;
		a.deadline = 0;
		a.recipient = 0;
		a.bidHash = 0;
		a.thirdParty = 0;
		a.thirdPartyFee = 0;
	}
}

This third party is a Courier service who receives it’s fee from the contract the same way that Alice does – by retrieving the secret pre-image from Bob. It will only complete delivery when Bob hands over the correct secret pre-image.

There remains one problem with our contract – what if Alice changes her mind and decides not to send her Guitar to Bob. Bob’s money would currently be trapped for ever, so we need to setup the event with a time limit so that if Bob’s secret is not revealed within this time limit, he can reclaim his ether:

contract decentralisedAuction{
	struct auction {
		uint deadline;
		uint highestBid;
		address highestBidder;
		uint bidHash;
		address recipient;
		address thirdParty;
		uint thirdPartyFee;
		uint deliveryDeadline;
	}
	mapping(uint => auction) auctions;
	uint numAuctions;

	function startAuction(uint timeLimit, address thirdParty, uint thirdPartyFee, uint deliveryDeadline) returns (uint auctionID){
		uint ID = numAuctions++;
		auction a = auctions[ID];
		a.deadline = block.number + timeLimit;
		a.recipient = msg.sender;
		a.thirdParty = thirdParty;
		a.thirdPartyFee = thirdPartyFee;
		a.deliveryDeadline = block.number + timeLimit + deliveryDeadline;
	}
	function bid(uint id, uint biddersHash) returns (address highestBidder){
		auction a = auctions[id];
		if (a.highestBid + 1*10^18 > msg.value || a.deadline > block.number) {
			msg.sender.send(msg.value);
			return a.highestBidder;
		}
		a.highestBidder.send(a.highestBid);
		a.highestBidder = msg.sender;
		a.highestBid = msg.value;
		a.bidHash  = biddersHash;
		return msg.sender;
	}
	function endAuction(uint id, uint key) returns (address highestBidder){
		auction a = auctions[id];
		if (block.number >= a.deadline && sha3(key) == a.bidHash) {
			a.recipient.send(a.highestBid-a.thirdPartyFee);
			a.thirdParty.send(a.thirdPartyFee);
			clean(id);
		}
	}
	function notDelivered(uint id) {
		auction a = auctions[id];
		if (block.number >= a.deliveryDeadline && msg.sender == a.highestBidder){
			a.highestBidder.send(a.highestBid);
			clean(id);
		}
	}
	function clean(uint id) private{
		auction a = Auctions[id];
		a.highestBid = 0;
		a.highestBidder =0;
		a.deadline = 0;
		a.deliveryDeadline = 0;
		a.recipient = 0;
		a.bidHash = 0;
		a.thirdPartyFee = 0;
		a.thirdParty = 0;
	}
}

So now we have a contract which is setup to handle real world transactions over time.

Other uses for secrets

Take a look at the below contract:

contract transfer{
	uint ownersHash;
	address owner;

	function transfer(){
		ownersHash = 0xae1d270a3b5a89864b7076af168cd361760a5e413f4939c9f5d4abd3c6e10ce0;
		owner = msg.sender;
	}
	function receive(uint key){
		if (sha3(key) == ownersHash){
			owner.send(this.balance);
		}
	}
}

It might not be obvious at first glance what this contract is. It’s constructor function stores a hash that was passed during deployment and the address of the sender. Its only function ‘receive’ sends the contracts balance to the contracts owner when the correct pre-image is given.

In fact this contract code is designed to be deployed twice by two different users. Their purpose is so that two users can exchange ether, lets look at the deployment step by step:

1. Alice deploys a copy of the contract to the blockchain along with zero ether – note that at this stage only Alice knows the ‘preimage’ of the ownersHash.

contract transfer{
	uint ownersHash;
	address owner;

	function transfer(){
		ownersHash = 0xae1d270a3b5a89864b7076af168cd361760a5e413f4939c9f5d4abd3c6e10ce0;
		owner = msg.sender;
	}
	function receive(uint key){
		if (sha3(key) == ownersHash){
			owner.send(this.balance);
		}
	}
}

2. Bob sees this contract on the blockchain and sends an exact copy of the contract – this also contains the same hash used in the first contract whose pre-image is still known only to Alice.

3. Alice now sends 500 ether to Bob’s contract and waits. Bob sees this and sends 500 ether to Alice’s contract.

4. Because Alice is in possesion of the secret key she can send a transaction containing the key to her newly funded contract, and have this contract send her the ether.

5. By sending this transaction Alice has revealed the pre-image which Bob can then use in his contract to have the contract send him the ether.

You might be wondering why we would use such a convoluted method to transfer ether between two parties when it would be much easier to use a single contract – it is however a very important technique as it does not require the contracts be running on the same blockchain.

This method could quite easily be modified to allow the trustless exchange of other crypto currencies between chains.

This is a very useful method when you are building dapps that involve multiple parties as publishing the pre-images of a hash is much simpler than signing transactions with private keys.

Leave a comment