NEW BLOG POSTS
UTXO on Ethereum? Here’s How We Did It!
Josie Wilden
-
8 November 2024
Bringing Bitcoin’s Ownership Model to Ethereum’s Smart Contracts.
Introduction
In the blockchain world, different networks have their own ways of handling ownership and transactions. Bitcoin, for instance, relies on the UTXO (Unspent Transaction Output) model, while Ethereum goes for an account-based approach. It’s not just a technical difference — it shapes how transactions get processed, validated, and stored. So what happens if we want UTXO functionality on Ethereum? That’s exactly what we took on in the Universal Blockchain Asset (UBA) project.
The Motivation
To showcase the UBA project — a demo exploring cross-chain asset management — we delved into implementing a UTXO model on Ethereum. This work was featured in Owen Vaughn’s presentation, “What if Your Blockchain is Different to Mine?” at the London Blockchain Conference, May 2024. You can read more about it here: Universal Blockchain Assets
UTXO Blockchains
The main advantage of UTXOs over account-based models lies in the concept of “spent” and “unspent” states:
- Ownership Tracking: Ownership is represented by an unspent transaction output (UTXO), which is tied to a specific Bitcoin address. Ownership changes only when the UTXO is spent.
- Asset Transfer: The owner of an unspent UTXO, verified by the corresponding private key, can use it to transfer ownership of the asset to another address.
Account-Based Blockchains
In contrast, account-based blockchains like Ethereum function differently:
- Ownership Tracked by Balances: Ownership is represented by an account balance rather than by individual transaction outputs
- Transaction Updates: When a transaction occurs, the sender’s balance decreases, and the recipient’s balance increases. There is no “spent” or “unspent” state for specific outputs.
This difference in design led us to simulate the UTXO model on Ethereum using a smart contract, allowing us to replicate the ownership logic of UTXOs within Ethereum’s account-based system.
Building the UTXO Model
To emulate UTXO functionality on Ethereum, we built a smart contract that essentially mimics the UTXO model. At its core, the contract allows us to create UTXOs, track their status, and “spend” them, all while using Ethereum’s account-based system.
Here’s a snippet of the UTXO structure:
contract UTXOModel {
struct UTXO {
bool
spent;
// True if spent; False if available
string
cpid;
// unique identifier for tracking the UTXO
address owner;
// Ethereum address of the owner
}
mapping(bytes32 => UTXO)
public
utxos;
// mapping of UTXO_keys:UTXO_value
The cpid (originally standing for “commitment packet”) is a unique identifier used to track the UTXO within the UBA project. It serves as a reference to metadata and links the UTXO to UBA.
The contract uses a UTXO struct that tracks whether the UTXO has been spent, stores a unique identifier (for tracking in the UBA project application code), and records the current owner's address.
The utxos mapping stores each UTXO’s unique identifier and associates it with the corresponding UTXO struct.
Requirements
To implement the UTXO model securely on Ethereum, we focused on two primary considerations:
- Ensuring the uniqueness of each UTXO ID: Since Ethereum doesn’t natively support the UTXO model, we use keccak256 hashing to generate unique UTXO identifiers based on the sender’s address and transaction timestamp.*
- Security: The UTXO model requires precise ownership tracking, which is enforced by our smart contract. Only the UTXO’s owner (as verified by their private key) can spend the UTXO. This prevents unauthorized spending, even if the UTXO ID is known.
Functions
You can find the full contract and its details on our GitHub repository, and the deployed contract is available on Sepolia, where you can check it out on Etherscan.
createUTXO()
- This function creates a new UTXO by generating a unique ID based on the caller’s address and a timestamp. The UTXO is initially marked as “unspent”.
- Cost: This function will cost Ether, as it modifies the state by adding a new UTXO entry to the utxos mapping.
function
createUTXO
()
external
{
bytes32 utxoId = keccak256(abi.encodePacked(msg.sender, block.timestamp));
utxos[utxoId] = UTXO(
false
,
""
, msg.sender);
emit UTXOCreated(utxoId);
}
spendUTXO()
- This function marks an existing UTXO as “spent” and assigns a payload identifier (cpid). Only the owner of the UTXO can perform this action, ensuring ownership consistency.
- Cost: This function will cost Ether, as it involves modifying the state of the UTXO in the contract.
function
spendUTXO
(
bytes32 utxoId,
string
memory cpid
)
external
{
require
(utxos[utxoId].owner == msg.sender,
"Only the owner can spend the UTXO"
);
require
(!utxos[utxoId].spent,
"UTXO already spent"
);
utxos[utxoId].cpid = cpid;
utxos[utxoId].spent =
true
;
emit UTXOSpent(utxoId);
}
isUTXOSpent()
- This function allows external parties to check if a UTXO has been spent.
- Cost: This function does not cost Ether, as it is a read-only operation.
function
isUTXOSpent
(
bytes32 utxoId
)
external
view
returns
(
bool
) {
return
utxos[utxoId].spent;
}
getCpid()
- This function allows external parties to get the associated payload identifier (cpid) of a UTXO.
- Cost: This function does not cost Ether, as it is a read-only operation.
function
getCpid
(
bytes32 utxoId
)
external
view
returns
(
string
memory
) {
return
utxos[utxoId].cpid;
}
Conclusion
Simulating UTXO behaviour on Ethereum is an interesting challenge — adapting a concept from one blockchain model to function within another. For the UBA project, this approach gives us better control over asset tracking and ensures clear ownership within an Ethereum smart contract, while still leveraging the native blockchain’s essential features, like double-spend protection.
It really shows off how Solidity and Ethereum’s smart contract flexibility can help link up different blockchain models, bringing some of the best parts of UTXO to an account-based system.
Take a look at the code in action and have a peek at our contract details on GitHub and Etherscan. Hopefully, this little cross-model mash-up gives you a clearer view of how the UBA project works and sparks some ideas for your own blockchain projects!
Additional Notes
UTXO ID Generation: UTXO IDs are generated using keccak256
based on the sender’s address and block.timestamp
, which the miner sets when confirming the transaction. Although rare, there’s a minor chance of conflict if a miner adjusts timestamps within the allowed range. For applications needing extra security: adding a nonce or a unique transaction identifier to the ID generation could further ensure uniqueness.
Disclaimer: This smart contract is for demonstration purposes only. It simulates a UTXO model on Ethereum, but it is not intended for use in production environments.