Skip to content
This repository was archived by the owner on Feb 3, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,984 changes: 1,890 additions & 1,094 deletions Cargo.lock

Large diffs are not rendered by default.

50 changes: 21 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
# UTXO on Substrate

A UTXO chain implementation on Substrate, with two self-guided workshops.Original [UXTO inspiration](https://github.com/0x7CFE/substrate-node-template/tree/utxo) by [Dmitriy Kashitsyn](https://github.com/0x7CFE).
A UTXO chain implementation on Substrate, with two self-guided workshops. Original [UXTO inspiration](https://github.com/0x7CFE/substrate-node-template/tree/utxo) by [Dmitriy Kashitsyn](https://github.com/0x7CFE).

Substrate Version: `2.0.0-rc2`. For educational purposes only.
Substrate Version: `2.0.0-rc6`. For educational purposes only.

## Table of Contents
- [Installation](#Installation): Setting up Rust & Substrate dependencies

- [UI Demo](#UI-Demo): Demoing this UTXO implementation in a simple UI
- [UI Demo](#UI-Demo): Demo of UTXO implementation in a simple UI

- [Beginner Workshop](#Beginner-Workshop): A self guided, 1 hour workshop that familiarizes you with Substrate basics
- [Beginner Workshop](#Beginner-Workshop): A self guided, 1 hour workshop that familiarizes you with Substrate.

- [Advanced Workshop](#Advanced-Workshop): A self guided, 2 hour video tutorial, that teaches you how to build this UTXO blockchain from scratch
- [Advanced Workshop](#Advanced-Workshop): A self guided, 2 hour video tutorial, that teaches you how to build a UTXO blockchain from scratch.

- [Helpful Resources](#Helpful-Resources): Documentation and references if you get stuck in the workshops
- [Helpful Resources](#Helpful-Resources): Additional supporting documentation and references for troubleshooting.


## Installation

### 1. Install or update Rust
```zsh
```bash
curl https://sh.rustup.rs -sSf | sh

# On Windows, download and run rustup-init.exe
Expand All @@ -28,47 +28,39 @@ curl https://sh.rustup.rs -sSf | sh
rustup update nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
rustup update stable
cargo install --git https://github.com/alexcrichton/wasm-gc
```

### 2. Clone this workshop

Clone your copy of the workshop codebase

```zsh
```bash
git clone https://github.com/substrate-developer-hub/utxo-workshop.git
```

## UI Demo

In this UI demo, you will interact with the UTXO blockchain via the [Polkadot UI](https://substrate.dev/docs/en/development/front-end/polkadot-js).

The following demo takes you through a scenario where:
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

1. Compile and build a release in dev mode
```
# Initialize your Wasm Build environment:
./scripts/init.sh

# Build Wasm and native code:
1. Compile and build a release node
```bash
cargo build --release
```

2. Start your node & start producing blocks:
```zsh
./target/release/utxo-workshop --dev

# If you already modified state, run this to purge the chain
./target/release/utxo-workshop purge-chain --dev
2. Start a node. The `--dev` flag will start a single mining node, and the `--tmp` flag will start it in a new temporary directory.
```bash
./target/release/utxo-workshop --dev --tmp
```

3. In the console, notice the helper printouts. In particular, notice the default account `Alice` was already has `100 UTXO` upon the genesis block.
3. In the console note the helper printouts. In particular, notice the default account `Alice` already has `100 UTXO` within the genesis block.

4. Open [Polkadot JS](https://polkadot.js.org/apps/#/settings), making sure the client is connected to your local node by going to Settings > General, and selecting `Local Node` in the `remote node` dropdown.
4. Open [Polkadot JS](https://polkadot.js.org/apps/#/settings) making sure the client is connected to your local node by going to Settings > General and selecting `Local Node` in the `remote node` dropdown.

5. **Declare the custom datatypes in PolkadotJS**, since the JS client cannot automatically infer this from the UTXO module. Go to Settings > Developer tab and paste in the following JSON:
5. **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:

```json
{
Expand Down Expand Up @@ -109,7 +101,7 @@ cargo build --release
- value: `50`
- pubkey: `0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48`

Send this as an `unsigned` transaction. With UTXO blockchains, the proof is already in the `sigscript` input.
Send as an `unsigned` transaction. With UTXO blockchains, the proof is already in the `sigscript` input.

8. **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.

Expand All @@ -118,12 +110,12 @@ cargo build --release
## Beginner Workshop
**Estimated time**: 2 hours

In this workshop, you will:
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:
1. The Rust compiler compiles without errors
1. The Rust compiler compiles without any errors
2. All tests in `utxo.rs` pass, ensuring secure transactions

### Directions
Expand All @@ -134,7 +126,7 @@ git fetch origin workshop:workshop
git checkout workshop
```

2. Cd into the base directory. Try running the test with: `cargo test -p utxo-runtime`.
2. Cd into the base directory. Run the test using: `cargo test -p utxo-runtime`.

```zsh
compiling utxo-runtime v2.0.0 (/Users/nicole/Desktop/utxo-workshop/runtime)
Expand Down
42 changes: 21 additions & 21 deletions node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ build = 'build.rs'
edition = '2018'
name = 'utxo-workshop'
repository = 'https://github.com/substrate-developer-hub/utxo-workshop'
version = '2.0.0-rc2'
version = '2.0.0-rc6'

[build-dependencies]
substrate-build-script-utils = '2.0.0-rc2'
substrate-build-script-utils = '2.0.0-rc6'

[dependencies.utxo-runtime]
path = '../runtime'
version = '2.0.0-rc2'
version = '2.0.0-rc6'

[dependencies]
futures = '0.3.1'
Expand All @@ -20,24 +20,24 @@ structopt = '0.3.8'
parity-scale-codec = '1.3.0'
sha3 = "0.8"
rand = { version = "0.7", features = ["small_rng"] }
sc-consensus-pow = '0.8.0-rc2'
sc-basic-authorship = '0.8.0-rc2'
sc-cli = '0.8.0-rc2'
sc-client-api = '2.0.0-rc2'
sc-consensus = '0.8.0-rc2'
sc-executor = '0.8.0-rc2'
sc-network = '0.8.0-rc2'
sc-service = '0.8.0-rc2'
sc-transaction-pool = '2.0.0-rc2'
sp-api = '2.0.0-rc2'
sp-blockchain = '2.0.0-rc2'
sp-consensus = '0.8.0-rc2'
sp-consensus-pow = '0.8.0-rc2'
sp-core = '2.0.0-rc2'
sp-inherents = '2.0.0-rc2'
sp-runtime = '2.0.0-rc2'
sp-timestamp = '2.0.0-rc2'
sp-transaction-pool = '2.0.0-rc2'
sc-consensus-pow = '0.8.0-rc6'
sc-basic-authorship = '0.8.0-rc6'
sc-cli = '0.8.0-rc6'
sc-client-api = '2.0.0-rc6'
sc-consensus = '0.8.0-rc6'
sc-executor = '0.8.0-rc6'
sc-network = '0.8.0-rc6'
sc-service = '0.8.0-rc6'
sc-transaction-pool = '2.0.0-rc6'
sp-api = '2.0.0-rc6'
sp-blockchain = '2.0.0-rc6'
sp-consensus = '0.8.0-rc6'
sp-consensus-pow = '0.8.0-rc6'
sp-core = '2.0.0-rc6'
sp-inherents = '2.0.0-rc6'
sp-runtime = '2.0.0-rc6'
sp-timestamp = '2.0.0-rc6'
sp-transaction-pool = '2.0.0-rc6'
sha3pow = {path = './sha3pow'}
hex = "0.4"

Expand Down
18 changes: 9 additions & 9 deletions node/sha3pow/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
authors = ['Anonymous']
edition = '2018'
name = 'sha3pow'
version = '2.0.0-rc2'
version = '2.0.0-rc6'

[dependencies]
parity-scale-codec = '1.3.0'
sha3 = "0.8"
rand = { version = "0.7", features = ["small_rng"] }
sc-consensus = '0.8.0-rc2'
sc-consensus-pow = '0.8.0-rc2'
sc-client-api = '2.0.0-rc2'
sp-api = '2.0.0-rc2'
sp-blockchain = '2.0.0-rc2'
sp-consensus-pow = '0.8.0-rc2'
sp-core = '2.0.0-rc2'
sp-runtime = '2.0.0-rc2'
sc-consensus = '0.8.0-rc6'
sc-consensus-pow = '0.8.0-rc6'
sc-client-api = '2.0.0-rc6'
sp-api = '2.0.0-rc6'
sp-blockchain = '2.0.0-rc6'
sp-consensus-pow = '0.8.0-rc6'
sp-core = '2.0.0-rc6'
sp-runtime = '2.0.0-rc6'
93 changes: 2 additions & 91 deletions node/sha3pow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,90 +52,6 @@ impl Compute {
}
}

/// A minimal PoW algorithm that uses Sha3 hashing.
/// Difficulty is fixed at 1_000_000
#[derive(Clone)]
pub struct MinimalSha3Algorithm;

// Here we implement the general PowAlgorithm trait for our concrete Sha3Algorithm
impl<B: BlockT<Hash=H256>> PowAlgorithm<B> for MinimalSha3Algorithm {
type Difficulty = U256;

fn difficulty(&self, _parent: B::Hash) -> Result<Self::Difficulty, Error<B>> {
// Fixed difficulty hardcoded here
Ok(U256::from(1_000_000))
}

fn verify(
&self,
_parent: &BlockId<B>,
pre_hash: &H256,
seal: &RawSeal,
difficulty: Self::Difficulty
) -> Result<bool, Error<B>> {
// Try to construct a seal object by decoding the raw seal given
let seal = match Seal::decode(&mut &seal[..]) {
Ok(seal) => seal,
Err(_) => return Ok(false),
};

// See whether the hash meets the difficulty requirement. If not, fail fast.
if !hash_meets_difficulty(&seal.work, difficulty) {
return Ok(false)
}

// Make sure the provided work actually comes from the correct pre_hash
let compute = Compute {
difficulty,
pre_hash: *pre_hash,
nonce: seal.nonce,
};

if compute.compute() != seal {
return Ok(false)
}

Ok(true)
}

fn mine(
&self,
_parent: &BlockId<B>,
pre_hash: &H256,
difficulty: Self::Difficulty,
round: u32 // The number of nonces to try during this call
) -> Result<Option<RawSeal>, Error<B>> {
// Get a randomness source from the environment; fail if one isn't available
let mut rng = SmallRng::from_rng(&mut thread_rng())
.map_err(|e| Error::Environment(format!("Initialize RNG failed for mining: {:?}", e)))?;

// Loop the specified number of times
for _ in 0..round {

// Choose a new nonce
let nonce = H256::random_using(&mut rng);

// Calculate the seal
let compute = Compute {
difficulty,
pre_hash: *pre_hash,
nonce,
};
let seal = compute.compute();

// If we solved the PoW then return, otherwise loop again
if hash_meets_difficulty(&seal.work, difficulty) {
return Ok(Some(seal.encode()))
}
}

// Tried the specified number of rounds and never found a solution
Ok(None)
}
}



/// A complete PoW Algorithm that uses Sha3 hashing.
/// Needs a reference to the client so it can grab the difficulty from the runtime.
pub struct Sha3Algorithm<C> {
Expand Down Expand Up @@ -175,6 +91,7 @@ impl<B: BlockT<Hash=H256>, C> PowAlgorithm<B> for Sha3Algorithm<C> where
&self,
_parent: &BlockId<B>,
pre_hash: &H256,
_pre_digest: Option<&[u8]>,
seal: &RawSeal,
difficulty: Self::Difficulty
) -> Result<bool, Error<B>> {
Expand Down Expand Up @@ -207,6 +124,7 @@ impl<B: BlockT<Hash=H256>, C> PowAlgorithm<B> for Sha3Algorithm<C> where
&self,
_parent: &BlockId<B>,
pre_hash: &H256,
_pre_digest: Option<&[u8]>,
difficulty: Self::Difficulty,
round: u32 // The number of nonces to try during this call
) -> Result<Option<RawSeal>, Error<B>> {
Expand Down Expand Up @@ -238,10 +156,3 @@ impl<B: BlockT<Hash=H256>, C> PowAlgorithm<B> for Sha3Algorithm<C> where
Ok(None)
}
}

#[test]
fn clone_works() {
let client = Arc::new(());
let algo1 = Sha3Algorithm::<()>::new(client);
let algo2 = algo1.clone();
}
2 changes: 1 addition & 1 deletion node/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ fn testnet_genesis(
println!("NEW UTXO HASH in UTXOStore onchain: 0xdbc75ab8ee9b83dcbcea4695f9c42754d94e92c3c397d63b1bc627c2a2ef94e6\n");

GenesisConfig {
system: Some(SystemConfig {
frame_system: Some(SystemConfig {
code: WASM_BINARY.to_vec(),
changes_trie_config: Default::default(),
}),
Expand Down
Loading