A UTXO chain implementation on Substrate, with two self-guided workshops. Original UXTO inspiration by Dmitriy Kashitsyn.
Substrate Version: 2.0.0-rc6
. For educational purposes only.
-
Installation: Setting up Rust & Substrate dependencies
-
UI Demo: Demo of UTXO implementation in a simple UI
-
Beginner Workshop: A self guided, 1 hour workshop that familiarizes you with Substrate.
-
Advanced Workshop: A self guided, 2 hour video tutorial, that teaches you how to build a UTXO blockchain from scratch.
-
Helpful Resources: Additional supporting documentation and references for troubleshooting.
curl https://sh.rustup.rs -sSf | sh
# On Windows, download and run rustup-init.exe
# from https://rustup.rs instead
rustup update nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
rustup update stable
Clone your copy of the workshop codebase
git clone https://github.com/substrate-developer-hub/utxo-workshop.git
In this UI demo, you will interact with the UTXO blockchain via the Polkadot UI.
The following example takes you through a scenario where:
- Alice already owns a UTXO of value 100 upon genesis
- Alice sends Bob a UTXO with value 50, tipping the remainder to validators
- Compile and build a release node
cargo build --release
- Start a node. The
--dev
flag will start a single mining node, and the--tmp
flag will start it in a new temporary directory.
./target/release/utxo-workshop --dev --tmp
-
In the console note the helper printouts. In particular, notice the default account
Alice
already has100 UTXO
within the genesis block. -
Open Polkadot JS making sure the client is connected to your local node by going to Settings > General and selecting
Local Node
in theremote node
dropdown. -
Declare custom datatypes in PolkadotJS as the frontend cannot automatically detect this information. To do this, go to Settings > Developer tab and paste in the following JSON:
{
"Address": "AccountId",
"LookupSource": "AccountId",
"Value": "u128",
"TransactionInput": {
"outpoint": "Hash",
"sigscript": "H512"
},
"TransactionOutput": {
"value": "Value",
"pubkey": "Hash"
},
"Transaction": {
"inputs": "Vec<TransactionInput>",
"outputs": "Vec<TransactionOutput>"
},
"Difficulty": "U256",
"DifficultyAndTimestamp": {
"difficulty": "Difficulty",
"timestamp": "Moment"
},
"Public": "H256"
}
-
Confirm that Alice already has 100 UTXO at genesis. In
Chain State
>Storage
, selectutxo
. Input the hash0x76584168d10a20084082ed80ec71e2a783abbb8dd6eb9d4893b089228498e9ff
. Click the+
notation to query blockchain state.Notice that:
- This UTXO has a value of
100
- This UTXO belongs to Alice's pubkey. You use the subkey tool to confirm that the pubkey indeed belongs to Alice
- This UTXO has a value of
-
Spend Alice's UTXO, giving 50 to Bob. In the
Extrinsics
tab, invoke thespend
function from theutxo
pallet, using Alice as the transaction sender. Use the following input parameters:- outpoint:
0x76584168d10a20084082ed80ec71e2a783abbb8dd6eb9d4893b089228498e9ff
- sigscript:
0x6ceab99702c60b111c12c2867679c5555c00dcd4d6ab40efa01e3a65083bfb6c6f5c1ed3356d7141ec61894153b8ba7fb413bf1e990ed99ff6dee5da1b24fd83
- value:
50
- pubkey:
0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48
Send as an
unsigned
transaction. With UTXO blockchains, the proof is already in thesigscript
input. - outpoint:
-
Verify that your transaction succeeded. In
Chain State
, look up the newly created UTXO hash:0xdbc75ab8ee9b83dcbcea4695f9c42754d94e92c3c397d63b1bc627c2a2ef94e6
to verify that a new UTXO of 50, belonging to Bob, now exists! Also you can verify that Alice's original UTXO has been spent and no longer exists in UtxoStore.
Coming soon: A video walkthrough of the above demo.
Estimated time: 2 hours
In this workshop you will:
- Get familiar with basic Rust and Substrate functionality
- Prevent malicious users from sending bad UTXO transactions
Your challenge is to fix the code such that:
- The Rust compiler compiles without any errors
- All tests in
utxo.rs
pass, ensuring secure transactions
- Checkout the
workshop
branch. TheMaster
branch has the solutions, so don't peek!
git fetch origin workshop:workshop
git checkout workshop
- Cd into the base directory. Run the test using:
cargo test -p utxo-runtime
.
compiling utxo-runtime v2.0.0 (/Users/nicole/Desktop/utxo-workshop/runtime)
error[E0433]: failed to resolve: use of undeclared type or module `H512`
--> /Users/nicole/Desktop/utxo-workshop/runtime/src/utxo.rs:236:31
|
236 | input.sigscript = H512::zero();
| ^^^^ use of undeclared type or module `H512`
...
-
Your first task: fix all the compiler errors! Hint: Look for the
TODO
comments inutxo.rs
to see where to fix errors. -
Once your code compiles, it's now time to fix the
8
failing tests!
failures:
utxo::tests::attack_by_double_counting_input
utxo::tests::attack_by_double_generating_output
utxo::tests::attack_by_over_spending
utxo::tests::attack_by_overflowing_value
utxo::tests::attack_by_permanently_sinking_outputs
utxo::tests::attack_with_empty_transactions
utxo::tests::attack_with_invalid_signature
utxo::tests::test_simple_transaction
- In
utxo.rs
, edit the logic invalidate_transaction()
function to make all tests pass.
running 8 tests
test utxo::tests::attack_by_overflowing_value ... ok
test utxo::tests::attack_by_double_counting_input ... ok
test utxo::tests::attack_by_double_generating_output ... ok
test utxo::tests::attack_by_over_spending ... ok
test utxo::tests::attack_with_empty_transactions ... ok
test utxo::tests::attack_with_invalid_signature ... ok
test utxo::tests::attack_by_permanently_sinking_outputs ... ok
test utxo::tests::test_simple_transaction ... ok
VIDEO TUTORIALS COMING SOON
Estimated time: 2 hours
In this workshop, you will implement this UTXO project from scratch using Substrate.
You will learn:
- How to implement the UTXO ledger model on Substrate
- How to secure UTXO transactions against attacks
- How to seed genesis block with UTXOs
- How to reward block validators in this environment
- How to customize transaction pool logic on Substrate
- Good coding patterns for working with Substrate & Rust, including testing and refactoring
Checkout the startercode
branch to get the boilerplate for this workshop.
git fetch origin startercode:startercode
git checkout startercode