{"id":18637884,"url":"https://github.com/ddimaria/rust-blockchain-tutorial","last_synced_at":"2025-07-06T08:06:12.472Z","repository":{"id":67523608,"uuid":"340988285","full_name":"ddimaria/rust-blockchain-tutorial","owner":"ddimaria","description":"This repo aims to train Rust developers on intermediate and advanced practices to help grok fundamental concepts in Ethereum blockchains.  It includes functioning nodes, WASM contracts and execution runtime, and a Web3 client for interacting with the chain.","archived":false,"fork":false,"pushed_at":"2023-03-12T18:11:21.000Z","size":508,"stargazers_count":29,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-11T16:15:28.207Z","etag":null,"topics":["blockchain","dapp","ethereum","rust","wasm"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ddimaria.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-21T19:42:49.000Z","updated_at":"2025-03-25T11:06:25.000Z","dependencies_parsed_at":null,"dependency_job_id":"9a96a893-c01e-40cb-97bb-8780ad963893","html_url":"https://github.com/ddimaria/rust-blockchain-tutorial","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ddimaria/rust-blockchain-tutorial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddimaria%2Frust-blockchain-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddimaria%2Frust-blockchain-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddimaria%2Frust-blockchain-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddimaria%2Frust-blockchain-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ddimaria","download_url":"https://codeload.github.com/ddimaria/rust-blockchain-tutorial/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddimaria%2Frust-blockchain-tutorial/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263868243,"owners_count":23522317,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blockchain","dapp","ethereum","rust","wasm"],"created_at":"2024-11-07T05:38:12.857Z","updated_at":"2025-07-06T08:06:12.461Z","avatar_url":"https://github.com/ddimaria.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- omit in toc --\u003e\n# Rust Blockchain Tutorial\n\n_WORK IN PROGRESS_\n\nThis repo is designed to train Rust developers on intermediate and advanced Rust development and help understand the primary concepts in Ethereum blockchain development.\n\n\u003c!-- omit in toc --\u003e\n## Roadmap\n\n- [x] Ethereum Types\n- [x] Cryptography Primitives\n- [x] Chain Node\n- [x] Web3 Client\n- [x] WASM/WASI VM for Contract Execution (wasmtime)\n- [ ] Rust Smart Contracts\n  - [x] Base Implementation\n  - [ ] Fungible\n  - [ ] Non Fungible\n  - [ ] Multi Asset\n- [ ] P2P Networking between Nodes (libp2p)\n- [ ] PoS Consensus\n- [x] Persistent Disk Chain State (RocksDB)\n- [ ] Full Tutorial\n- [ ] CI\n\n\u003c!-- omit in toc --\u003e\n## Table of Contents\n\n- [Introduction](#introduction)\n- [Ethereum Primitives](#ethereum-primitives)\n  - [Accounts](#accounts)\n    - [Nonce](#nonce)\n  - [Transactions](#transactions)\n    - [Kinds of Transactions](#kinds-of-transactions)\n    - [Transaction Hashes](#transaction-hashes)\n  - [Blocks](#blocks)\n    - [Genesis Block](#genesis-block)\n- [Basic Cryptography](#basic-cryptography)\n  - [Public Key Cryptography](#public-key-cryptography)\n  - [Hashing](#hashing)\n  - [Address](#address)\n- [Web3 Client - An Introduction](#web3-client---an-introduction)\n- [The Life of a Transaction](#the-life-of-a-transaction)\n  - [Create a Transaction](#create-a-transaction)\n  - [Transaction Signing](#transaction-signing)\n  - [Submitting a Transaction](#submitting-a-transaction)\n  - [Receiving a Transaction](#receiving-a-transaction)\n  - [Verifying a Signed Transaction](#verifying-a-signed-transaction)\n  - [Add the Transaction to the Mempool](#add-the-transaction-to-the-mempool)\n  - [Kickoff the Transaction Processor](#kickoff-the-transaction-processor)\n  - [Processing Transactions](#processing-transactions)\n  - [Processing a Single Transaction](#processing-a-single-transaction)\n- [Organization](#organization)\n  - [Chain](#chain)\n    - [Sample API: eth\\_blockNumber](#sample-api-eth_blocknumber)\n      - [Request](#request)\n      - [Response](#response)\n  - [Runtime](#runtime)\n  - [Contracts](#contracts)\n    - [WIT](#wit)\n    - [Sample Contract - Erc20](#sample-contract---erc20)\n    - [Invoking a Contract Function](#invoking-a-contract-function)\n  - [Web3](#web3)\n    - [Sample Usage](#sample-usage)\n  - [Types](#types)\n  - [Crypto](#crypto)\n- [Getting Started](#getting-started)\n- [Compiling](#compiling)\n- [Running Tests](#running-tests)\n\n## Introduction\n\nWhen I first entered the crypto space, I had never used Rust and was unfamiliar with blockchain technology, let alone any knowledge of Ethereum concepts.  This is the tutorial I wish I had back then, and will hopefully guide Rust developers along their blockchain journey.\n\nWhile the concepts explored here are based on Ethereum, there are many instances where they diverge from it.  For example, structs are simplified to just show general concepts.  Different hashing and consensus algorithims are implemented.  The most divergent area are smart contracts.  We'll explore Rust-based smart contracts that run on a WASM virtual machine.  I went in this direction to keep the language choice homogenious.  The overall approach is similar to Solidity, though the implementation is very different.\n\n## Ethereum Primitives\n\n### Accounts\n\nIn Ethereum, `Accounts` are either `Externally Owned Accounts` or `Contract Accounts`.  Addresses are hex encoded: `0x71562b71999873DB5b286dF957af199Ec94617F7`.\n\n```rust\ntype Account = ethereum_types::Address;\n```\n\nFor a given address, data associated with the account is stored on chain:\n\n```rust\nstruct AccountData {\n    nonce: u64,\n    balance: u64,\n    code_hash: Option\u003cBytes\u003e,\n}\n\nimpl AccountData {\n    fn is_contract(\u0026self) -\u003e bool {\n        self.code_hash.is_some()\n    }\n}\n```\n\nExternally Owned Accounts are simply a public address.  This address is a 20 byte hash (`H160`), and is created by applying a hash function on the public key, taking the last 20 bytes.  This is how we create an Ethereum Account in Rust:\n\n```rust\nuse crypto::{keypair, public_key_address};\n\nlet (private_key, public_key) = keypair();\nlet address = public_key_address(\u0026public_key);\n\nfn public_key_address(key: \u0026PublicKey) -\u003e H160 {\n    let public_key = key.serialize_uncompressed();\n    let hash = hash(\u0026public_key[1..]);\n\n    Address::from_slice(\u0026hash[12..])\n}\n```\n\nPublic keys are not stored on the chain.  Since we can't derive the public key from the hash, the public key is not known until a signed transaction is validated.  We'll dig a bit more into this in the Transaction section.\n\nContract Accounts are also just an address, but have a code hash associated with them.  A contract's address is created by encoding the sender's address and their current nonce.  This encoding is then hashed using a hash function, taking the last 20 bytes.  This process is similiar to the Externally Owned Account creation, but the input is an encoded tuple.\n\n```rust\nuse crypto::{to_address};\nuse web3::web3;\n\nlet account = MY_ACCOUNT_ADDRESS;\nlet web3 = web3::Web3::new(\"http://127.0.0.1:8545\")?;\nlet nonce = web3().get_transaction_count(account).await.unwrap();\nlet serialized: Vec\u003cu8\u003e = bincode::serialize(\u0026(account, nonce)).unwrap();\nlet contract_address = to_address(\u0026serialized).unwrap();\n```\n\nIt's important to note that addresses (accounts) are iniatiated outside of a blockchain.  They can be generated in many ways, though the most common is to use a wallet.  In our examples, we'll sign them offline using the provided tools in the `crypto` crate.  Accounts are stored on the chain when they are used for the first time.\n\nAccounts are also deterministic.  That is, given the same inputs, the same address is always generated.\n\n#### Nonce\n\nAccounts have an associated `nonce`.  A nonce is an acronym for \"number only used once\" and is a counter of the number of processed transactions for a given account.  It is to be incremented every time a new transaction is submitted.  User's must keep track of their own nonces, though wallet providers can do this as well.  Anyone can query the blockchain for current nonce of an account (EOA and contract), which can be helpful for determining the next nonce to use.\n\nThe main purpose of a nonce is to make a data structure unique, so that each data structure is explicit regarding otherwise identical data being effectively different.  We'll discuss nonces more when breaking down transactions and how blockchain nodes use them to preserve the order of processing submitted transactions.\n\n### Transactions\n\nTransactions are the heart of a blockchain.  Without them, the chain's state would remain unchanged.  Transactions drive state changes.  They are submitted externally owned accounts only (i.e. not a contract account).\n\n```rust\npub struct Transaction {\n    pub from: Address,\n    pub to: Option\u003cAddress\u003e,\n    pub hash: Option\u003cH256\u003e,\n    pub nonce: Option\u003cU256\u003e,\n    pub value: U256,\n    pub data: Option\u003cBytes\u003e,\n    pub gas: U256,\n    pub gas_price: U256,\n}\n```\n\nWhile the `Transaction` data structure has much more fields in Ethereum than shown above, the data subset we're using is the minimum needed to understand transactions.\n\n* The `from` portion of a transaction identifies the transaction sender.  This account must already exist on the blockchain.\n* The `to` attribute represents the receiver of the value transferred in a transaction.  It can also be a contract address, where code is executed.  It is optional because it is left empty (or zero or null) to signify a transaction that deploys a contract.\n* A `hash` attribute contains the hash of the transaction.  It's optional so that it can be calculated after the other values in the transaction are populated.\n* The `nonce` is the sender's next account nonce.  It is the existing account nonce incremented by one.  Leaving this blank will let the blockchain autoincrement on behlaf of the transaction.\n* `value` indicates the amount of `coin` to transfer from the sender to the recipient.  This number can be zero for non-value-transferring transactions.\n* The `data` attribute can hold various pieces of data.  When deploying a contract, it holds bytes of the assembled contract code.  When executing a function on a contract, it holds the function name and parameters.  It can also be any piece of data that the sender wants to include in the transaction.\n* `gas` is the total number of units that the sender is offering to pay for the transaction.  We'll discuss this in more detail later.\n* The `gas_price` is the amount of `coin` (eth in Ethereum) to be paid for each unit of `gas`.\n\n#### Kinds of Transactions\n\nThere are 3 ways that transaction can be used:\n\n```rust\n\npub enum TransactionKind {\n    Regular(Address, Address, U256),\n    ContractDeployment(Address, Bytes),\n    ContractExecution(Address, Address, Bytes),\n}\n```\n\n* `Regular` transactions are ones where value is transferred from one account to another.\n* `Contract deployment` transactions are used to deploy contract code to the blockchain and are without a 'to' address, where the data field is used for the contract code.\n* `Contract execution` transactions interact with a deployed smart contract. In this case, 'to' address is the smart contract address.\n\nThe type of transaction is derived from the values in the transaction:\n\n```rust\nfn kind(self) -\u003e Result\u003cTransactionKind\u003e {\n    match (self.from, self.to, self.data) {\n        (from, Some(to), None) =\u003e Ok(TransactionKind::Regular(from, to, self.value)),\n        (from, None, Some(data)) =\u003e Ok(TransactionKind::ContractDeployment(from, data)),\n        (from, Some(to), Some(data)) =\u003e Ok(TransactionKind::ContractExecution(from, to, data)),\n        _ =\u003e Err(TypeError::InvalidTransaction(\"kind\".into())),\n    }\n}\n```\n\n#### Transaction Hashes\n\nOnce a transaction data structure is filled in, the hash can be calculated:\n\n```rust\nlet serialized = bincode::serialize(\u0026transaction)?;\nlet hash: H256 = hash(\u0026serialized).into();\n```\n\nWe first encode/serialize the transaction and then apply a hashing function.  To keep things simple, we're using [Bincode](https://github.com/bincode-org/bincode) to serialize and compress the data to a binary format throughout this blockchain.  Ethereum uses [RLP Encoding](https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/) for most of it's encoding/serialization.\n\n### Blocks\n\nBlocks essentially containers for validated transactions.  Once a set of transactions are validated, they are ready to be added to a block.\n\n```rust\nstruct Block {\n    number: U64,\n    hash: Option\u003cH256\u003e,\n    parent_hash: H256,\n    transactions: Vec\u003cTransaction\u003e,\n    transactions_root: H256,\n    state_root: H256,\n}\n```\n\nA Block `number` is an incremented unsigned integer.  All blocks are in order, and there are no missing blocks.  Blocks start at zero (called the `Genesis Block`).  Similar to transactions, blocks have a `hash` of their values:\n\n```rust\nlet serialized = bincode::serialize(\u0026block)?;\nlet hash: H256 = hash(\u0026serialized).into();\nblock.hash = Some(hash);\n```\n\nThe `parent_hash` is the hash of the previous block.  This links blocks together and are critical in blockchain validation.  All transactions are within the block and are needed for the block verification process.\n\nThe `transaction_root` is the Merkle Root of all of the transactions in the block:\n\n```rust\nfn to_trie(transactions: \u0026[Transaction]) -\u003e Result\u003cEthTrie\u003cMemoryDB\u003e\u003e {\n    let memdb = Arc::new(MemoryDB::new(true));\n    let mut trie = EthTrie::new(memdb);\n\n    transactions.iter().try_for_each(|transaction| {\n        trie.insert(\n            transaction.transaction_hash()?.as_bytes(),\n            bincode::serialize(\u0026transaction)?,\n        )?\n    })?;\n\n    Ok(trie)\n}\n\nfn root_hash(transactions: \u0026[Transaction]) -\u003e Result\u003cH256\u003e {\n    let mut trie = Transaction::to_trie(transactions)?;\n    let root_hash = trie.root_hash()?;\n\n    Ok(H256::from_slice(root_hash.as_bytes()))\n}\n```\n\nThe `state_root` is the Merkle Root of all state within the blockchain.  We'll detail this more when discussing Global State.\n\n#### Genesis Block\n\nThe first block in a blockchain is called the `genesis block`.  We're using a naive implementation to create an empty block with no state:\n\n```rust\nfn genesis() -\u003e Result\u003cSelf\u003e {\n    Block::new(U64::zero(), H256::zero(), vec![], H256::zero())\n}\n```\n\nIn Ethereum, the genesis block is created using a `genisis file`.  This file contains configuration information and details the accounts to create and the amount of Eth they each get.  This is where the initial Eth comes from, though additional Eth is created for miners.  The movement to Proof of Stake in Ethereum V2 transforms miners to validators, and changes the reward mechanism.  We'll talk more about this in the consensus section.\n\n## Basic Cryptography\n\nWe've talked about public and private keys, addresses, and hashes, but dive a little deeper into what they are.\n\n### Public Key Cryptography\n\nPublic Key Cryptography is asymetric encryption, where there are public and private keys (key pair).  This is different from symmetric encryption that uses the same key to encrypt and decrypt.\n\nThere are differnt flavors of public key cryptography.  In this project we're using [Secp256k1](https://en.bitcoin.it/wiki/Secp256k1), which works with [ECDSA](https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm) signatures.  Secp256k1 is an elliptic curve.  We're not going to get into anything that complicated here, so just picture a line that's shaped like a door knob.  The public and private keys are just points on the curve.\n\nThe public key is derived from the private key.  A key is just a number (unsigned 256-bit in our case).  While you can derive a public key from a private key, you cannot create a private key from a public one.\n\nHere's how the encryption works.  Bob wants to send Alice a secret message.  He first asks Alice for her public key, which he uses to encrypt the message.  He then sends the message to Alice and she uses her private key to decrypt the message.  If anyone intercepts the message, they can't read it unless they have Alice's private key.  Alice knows that the message was not altered and she can prove that Bob sent her a message.\n\n```rust\nuse utils::crypto::keypair;\n\nlet (private_key, public_key) = keypair();\nprintln!(\"{:?}\", keypair());\n\nfn keypair() -\u003e (SecretKey, PublicKey) {\n    generate_keypair(\u0026mut rand::thread_rng())\n}\n```\n\nOutputs:\n\n```txt\nSecretKey(#2fd3a5f2b1f52597)\nPublicKey(6dfc30040b48fefe3e3cebe0ca2d5033189a93b50aeaa1876d21ea15fc756b6e3e8e48997fb1441464e254877fe898f6566488d2b0d578f2fbd31b48772be593)\n```\n\n### Hashing\n\nThe concept of a hash is fairly straightforward.  A hash function accepts any size data and outputs a number of fixed length.  Anytime the input is the same, the output (e.g. the hash) will always be the same.  Another key component is that you cannot derive the original data from the hash.\n\nHashes are used everywhere in blockchains.  They allow the formal verification that the source data was used to create it.  They are convenient ways of storing a proof of the input, without the burden of storing the data.\n\n```rust\nlet public_key = 6dfc30040b48fefe3e3cebe0ca2d5033189a93b50aeaa1876d21ea15fc756b6e3e8e48997fb1441464e254877fe898f6566488d2b0d578f2fbd31b48772be593;\nlet hash = hash(\u0026public_key[1..]);\nprintln!(\"{:?}\", hash);\n```\n\nOutputs:\n\n```txt\n[101, 35, 44, 44, 55, 13, 158, 100, 34, 209, 215, 186, 89, 105, 196, 45, 127, 154, 217, 113, 203, 127, 236, 66, 153, 233, 137, 207, 48, 140, 166, 244]\n```\n\n### Address\n\nWe already know that an account on Ethereum is just an address, but what is an address?  It's simply the trailing 20 bytes from a hash.\n\n```rust\nlet public_key = 6dfc30040b48fefe3e3cebe0ca2d5033189a93b50aeaa1876d21ea15fc756b6e3e8e48997fb1441464e254877fe898f6566488d2b0d578f2fbd31b48772be593;\nlet hash = hash(\u0026public_key[1..]);\nlet address = Address::from_slice(\u0026hash[12..]);\nprintln!(\"{:?}\", address);\n```\n\nOutputs:\n\n```txt\n0x5969c42d7f9ad971cb7fec4299e989cf308ca6f4\n```\n\n## Web3 Client - An Introduction\n\nEthereum chains expose a [json-rpc](https://www.jsonrpc.org/) interface.  Here's how you get an account's balance:\n\n```shell\ncurl -X POST \\\n     -H 'Content-Type: application/json' \\\n     -d '{\"jsonrpc\":\"2.0\",\"id\":\"id\",\"method\":\"eth_getBalance\",\"params\":[\"0xfbb55f17b2926063ae3fa5647c98eb1fac88c99e\"]}' \\\n     http://127.0.0.1:8545\n```\n\nThat returns:\n\n```json\n{\n    \"jsonrpc\":\"2.0\",\n    \"id\":\"id\",\n    \"result\":100\n}\n```\n\nThat wasn't very hard, but we want to interact with the API in a simpler, more type-safe way.  \n\nWeb3 clients are just SDKs that make it easier to interact with a blockchain.  They transform the call above to:\n\n```rust\nlet web3 = web3::Web3::new(\"http://127.0.0.1:8545\")?;\nlet account: Account = Account::from_str(\"0xfbb55f17b2926063ae3fa5647c98eb1fac88c99e\")?;\nlet balance: U256 = web3.get_balance(account).await;\n```\n\nOutputs:\n\n```rust\nOk(9999870002304000000000)\n```\n\n## The Life of a Transaction\n\nAccounts create and sign transactions.  Transactions combine to from a Block.  Transactions are an essential piece of a blockchain, so let's examine how they are made and what happens to them on the blockchain.\n\n### Create a Transaction\n\nThe first step is to create a transaction.\n\n```rust\nimpl Transaction {\n    pub fn new(\n        from: Account,\n        to: Option\u003cAccount\u003e,\n        value: U256,\n        nonce: U256,\n        data: Option\u003cBytes\u003e,\n    ) -\u003e Result\u003cSelf\u003e {\n        let mut transaction = Self {\n            from,\n            to,\n            value,\n            nonce,\n            hash: None,\n            data,\n            gas: U256::from(10),\n            gas_price: U256::from(10),\n        };\n\n        let serialized = bincode::serialize(\u0026transaction)?;\n        let hash: H256 = hash(\u0026serialized).into();\n        transaction.hash = Some(hash);\n\n        Ok(transaction)\n    }\n}\n\nlet from = Account::from_str(\"0x4a0d457e884ebd9b9773d172ed687417caac4f14\")?;\nlet to = Account::from_str(\"0xfbb55f17b2926063ae3fa5647c98eb1fac88c99e\")?;\nlet transaction = Transaction::new(\n    from,\n    Some(to),\n    U256::from(10),\n    None,\n    None,\n);\n```\n\nIn this transaction, we're sending 10 coin from the `0x4a0d457e884ebd9b9773d172ed687417caac4f14` to the `0xfbb55f17b2926063ae3fa5647c98eb1fac88c99e` account.\n\nNow that we have a transaction, let's sign it.\n\n### Transaction Signing\n\nTransactions must be signed before they are submitted to the blockchain.\n\n```rust\nfn sign(\u0026self, key: SecretKey) -\u003e Result\u003cSignedTransaction\u003e {\n    let encoded = bincode::serialize(\u0026self)?;\n    let recoverable_signature = sign_recovery(\u0026encoded, \u0026key)?;\n    let (_, signature_bytes) = recoverable_signature.serialize_compact();\n    let Signature { v, r, s } = recoverable_signature.into();\n    let transaction_hash = hash(\u0026signature_bytes).into();\n\n    let signed_transaction = SignedTransaction {\n        v,\n        r,\n        s,\n        raw_transaction: encoded.into(),\n        transaction_hash,\n    };\n\n    Ok(signed_transaction)\n}\n```\n\nSigning is done with the account holder's private key in order to generate a recoverable signature of the transaction.  We'll discuss basic cryptography in more detail later, but a recoverable signature is one where a public key can be derived from the signature and message.  This is important as it's how the blockchain validates the transaction.  Once the public key is recovered, it is hashed and must match the `from` address of the transaction.\n\nThe resulting `SignedTransaction` data structure is represented as:\n\n```rust\nstruct SignedTransaction {\n    v: u64,\n    r: H256,\n    s: H256,\n    raw_transaction: Bytes,\n    transaction_hash: H256,\n}\n```\n\nThe `v`, `r`, and `s` values represent the digital signature.  The `v` attribute is the recovery id that is used to derive the account holder's public key.  `r` and `s` hold values related to the signature (`r` is the value and `s` is the proof).\n\nThe transaction encoded and compressed and is stored as bytes in the `raw_transaction` attribute.  This minimizes the footprint of the packet.\n\nThe `transaction_hash` will be the transaction id in the blockchain.  It serves many purposes, and can be used to validate that the reconstructed transaction wasn't tampered with.\n\nUsing the handy web3 client, we can now sign the created transaction:\n\n```rust\nlet secret_key;\nlet transaction = transaction().await;\nlet signed_transaction = web3().sign_transaction(transaction, secret_key);\n```\n\n### Submitting a Transaction\n\nWith a newly created transaction that is signed, all that is left is to submit it to the blockchain.\n\n```rust\nlet encoded = bincode::serialize(\u0026signed_transaction)?;\nlet response = web3().send_raw(encoded.into()).await;\n```\n\nThe chain accepts the transaction and responds with a transaction id, which is just the hash of the transaction.  Let's take a look at what happens on the blockchain.\n\n### Receiving a Transaction\n\nThe chain receives the raw transaction from the json-rpc API:\n\n```rust\nfn eth_send_raw_transaction(module: \u0026mut RpcModule\u003cContext\u003e) -\u003e Result\u003c()\u003e {\n    module.register_async_method(\n        \"eth_sendRawTransaction\",\n        move |params, blockchain| async move {\n            let raw_transaction = params.one::\u003cBytes\u003e()?;\n            let transaction_hash = blockchain\n                .lock()\n                .await\n                .send_raw_transaction(raw_transaction)\n                .await\n                .map_err(|e| Error::Custom(e.to_string()))?;\n\n            Ok(transaction_hash)\n        },\n    )?;\n\n    Ok(())\n}\n```\n\nThis function registers the `eth_sendRawTransaction` method, followed by a closure that handles the incomming request.  Now that we have the raw transaction bytes, let's take a peek at the `send_raw_transaction()` function.\n\n```rust\nasync fn send_raw_transaction(\u0026mut self, transaction: Bytes) -\u003e Result\u003cH256\u003e {\n    let signed_transaction: SignedTransaction = bincode::deserialize(\u0026transaction)?;\n    let transaction: Transaction = signed_transaction.clone().try_into()?;\n    let transaction_hash = transaction.transaction_hash()?;\n\n    Transaction::verify(signed_transaction, transaction.from).map_err(|e| {\n        ChainError::TransactionNotVerified(format!(\"{}: {}\", transaction_hash, e))\n    })?;\n\n    self.send_transaction(transaction.into()).await\n}\n```\n\n### Verifying a Signed Transaction\n\nThe chain first deserializes the raw bytes into a `SignedTransaction` struct.  To recap, that struct is:\n\n```rust\nstruct SignedTransaction {\n    v: u64,\n    r: H256,\n    s: H256,\n    raw_transaction: Bytes,\n    transaction_hash: H256,\n}\n```\n\nWith a signed transaction in place, we can now verify that the transaction is properly signed.\n\n```rust\npub fn verify(signed_transaction: SignedTransaction, address: Address) -\u003e Result\u003cbool\u003e {\n    let (message, recovery_id, signature_bytes) = Transaction::recover_pieces(signed_transaction)?;\n    let key = recover_public_key(\u0026message, \u0026signature_bytes, recovery_id.to_i32())?;\n    let verified = verify(\u0026message, \u0026signature_bytes, \u0026key)?;\n    let addresses_match = address == public_key_address(\u0026key);\n\n    Ok(verified \u0026\u0026 addresses_match)\n}\n\nfn recover_pieces(signed_transaction: SignedTransaction) -\u003e Result\u003c(Vec\u003cu8\u003e, RecoveryId, [u8; 64])\u003e {\n    let message = signed_transaction.raw_transaction.to_owned();\n    let signature: Signature = signed_transaction.into();\n    let recoverable_signature: RecoverableSignature = signature.try_into()?;\n    let (recovery_id, signature_bytes) = recoverable_signature.serialize_compact();\n\n    Ok((message.to_vec(), recovery_id, signature_bytes))\n}\n```\n\nWe get the message, the recovery id, and the signature bytes from the signed transaction.  We can then use those pieces to recover the public key.  We then verify the `signature` is a valid ECDSA signature for `message` using the public key. We now invoke the companion `verify()` function in the crypto utility:\n\n```rust\nfn verify(message: \u0026[u8], signature: \u0026[u8], key: \u0026PublicKey) -\u003e Result\u003cbool\u003e {\n    let message = hash_message(message)?;\n    let signature = EcdsaSignature::from_compact(signature)\n        .map_err(|e| UtilsError::VerifyError(e.to_string()))?;\n\n    Ok(CONTEXT.verify_ecdsa(\u0026message, \u0026signature, key).is_ok())\n}\n```\n\nWe can also verify that the public key address matches the `from` attribute of a transaction, which completes the transaction verification process.  \n\n### Add the Transaction to the Mempool\n\nNow we can safely add the transaction to the mempool:\n\n```rust\nasync fn send_transaction(\n    \u0026mut self,\n    transaction_request: TransactionRequest,\n) -\u003e Result\u003cH256\u003e {\n    let mut transaction: Transaction = transaction_request.try_into()?;\n    let account = self.accounts.get_account(\u0026transaction.from)?;\n    let nonce = transaction.nonce.unwrap_or_else(|| account.nonce + 1_u64);\n\n    transaction.nonce = Some(nonce);\n\n    // regenerate the transaction hash with the nonce in place\n    let transaction_hash = transaction.hash()?;\n\n    // add to the transaction mempool\n    self.transactions.lock().await.send_transaction(transaction);\n\n    Ok(transaction_hash)\n}\n```\n\nThe mempool is where pending transactions are stored while they wait to be processed. \n\n### Kickoff the Transaction Processor\n\nTransactions are processed by a timer running in a separate Tokio thread every second:\n\n```rust\nlet transaction_processor = task::spawn(async move {\n    let mut interval = time::interval(Duration::from_millis(1000));\n\n    loop {\n        interval.tick().await;\n\n        if let Err(error) = blockchain_for_transaction_processor\n            .lock()\n            .await\n            .process_transactions()\n            .await\n        {\n            tracing::error!(\"Error processing transactions {}\", error.to_string());\n        }\n    }\n});\n```\n\nThis kicks off transaction processing.  We don't want our processor to panic at any point, so errors that occur while processing transactions are simply logged out.\n\n### Processing Transactions\n\nIn our naive implementation, we're ignoring gas and just processing transactions in a first in, first out (FIFO) methodology.  Ethereum validators can optimize the processing order of transactions by evaluating the maximum amount of gas they can receive by processing any given transactions.\n\nWe first drain the mempool queue, which prevents the processor that runs 1 second later from competing with processing the same transactions.\n\n```rust\nasync fn process_transactions(\u0026mut self) -\u003e Result\u003c()\u003e {\n    let transactions = self\n        .transactions\n        .lock()\n        .await\n        .mempool\n        .drain(0..)\n        .collect::\u003cVecDeque\u003c_\u003e\u003e();\n\n    if !transactions.is_empty() {\n        let mut receipts: Vec\u003cTransactionReceipt\u003e = vec![];\n        let mut processed: Vec\u003cTransaction\u003e = vec![];\n\n        for mut transaction in transactions.into_iter() {\n            match self.process_transaction(\u0026mut transaction) {\n                Ok((transaction, transaction_receipt)) =\u003e {\n                    receipts.push(transaction_receipt);\n                    processed.push(transaction.to_owned());\n                }\n                Err(error) =\u003e {\n                    match error {\n                        // The nonce is too high, add back to the mempool\n                        ChainError::NonceTooHigh(_, _) =\u003e {\n                            self.transactions\n                                .lock()\n                                .await\n                                .mempool\n                                .push_back(transaction);\n                        }\n                        _ =\u003e {},\n                    }\n                }\n            }\n        }\n\n        // update world state\n        let state_trie = self.accounts.root_hash()?;\n        self.world_state.update_state_trie(state_trie);\n\n        let block = self.new_block(processed, state_trie)?;\n\n        // now add the block number and hash to the receipts\n        for mut receipt in receipts.into_iter() {\n            receipt.block_number = Some(BlockNumber(block.number));\n            receipt.block_hash = block.hash;\n\n            self.transactions\n                .clone()\n                .lock()\n                .await\n                .receipts\n                .insert(receipt.transaction_hash, receipt);\n        }\n    }\n\n    Ok(())\n}\n```\n\nWe'll talk about processing an individual transaction in the next section.  Errors in processing a transaction are ignored and discarded except if the account nonce of a transaction is higher than the next available nonce.  It's very possible that an account sent several transactions at once, and we don't want to throw that transaction away.  The transaction is simple added into the back of the mempool for later processing.\n\nOnce the blockchain processes a transaction, the world state is updated.  In our blockchain, the world state is simply the Merkel Root of all accounts on the blockchain.  This is essentially the proof of the state transaction between blocks.\n\nThe new block is then added to the blockchain.\n\n### Processing a Single Transaction\n\n```rust\nfn process_transaction\u003c'a\u003e(\n    \u0026mut self,\n    transaction: \u0026'a mut Transaction,\n) -\u003e Result\u003c(\u0026'a mut Transaction, TransactionReceipt)\u003e {\n    let value = transaction.value;\n    let mut contract_address: Option\u003cAccount\u003e = None;\n    let transaction_hash = transaction.transaction_hash()?;\n\n    // ignore transactions without a nonce\n    if let Some(nonce) = transaction.nonce {\n        // create the `to` account if it doesn't exist\n        if let Some(to) = transaction.to {\n            self.accounts.add_empty_account(\u0026to)?;\n        }\n\n        let kind = transaction.to_owned().kind()?;\n\n        match kind {\n            TransactionKind::Regular(from, to, value) =\u003e {\n                self.accounts.transfer(\u0026from, \u0026to, value)\n            }\n            TransactionKind::ContractDeployment(from, data) =\u003e {\n                contract_address = self.accounts.add_contract_account(\u0026from, data).ok();\n                Ok(())\n            }\n            TransactionKind::ContractExecution(_from, to, data) =\u003e {\n                let code = self\n                    .accounts\n                    .get_account(\u0026to)?\n                    .code_hash\n                    .ok_or_else(|| ChainError::NotAContractAccount(to.to_string()))?;\n                let (function, params): (\u0026str, Vec\u003c\u0026str\u003e) = bincode::deserialize(\u0026data)?;\n\n                // call the function in the contract\n                runtime::contract::call_function(\u0026code, function, \u0026params)\n                    .map_err(|e| ChainError::RuntimeError(to.to_string(), e.to_string()))\n            }\n        }?;\n\n        // update the nonce\n        self.accounts.update_nonce(\u0026transaction.from, nonce)?;\n\n        let transaction_receipt = TransactionReceipt {\n            block_hash: None,\n            block_number: None,\n            contract_address,\n            transaction_hash,\n        };\n\n        return Ok((transaction, transaction_receipt));\n    }\n\n    Err(ChainError::MissingTransactionNonce(\n        transaction_hash.to_string(),\n    ))\n}\n```\n\nThe first step is to add the `to` account to the account storage if it doesn't already exist.  The blockchain then evaluates the kind of transaction it's processing.  The simplest type of a transaction is the `regular` one, which is just a coin transfer.\n\nA contract deployment is fairly straightforward as well.  First, a contract account is created, using the `from` address and the current `nonce` as inputs to the address hash.\n\n```rust\npub fn add_contract_account(\u0026mut self, key: \u0026Account, data: Bytes) -\u003e Result\u003cAccount\u003e {\n    let nonce = self.get_account(key)?.nonce;\n    let serialized = bincode::serialize(\u0026(key, nonce))?;\n    let account = to_address(\u0026serialized);\n    let account_data = AccountData::new(Some(data));\n    self.add_account(\u0026account, \u0026account_data)?;\n\n    Ok(account)\n}\n```\n\nCode received gets added to the `to` account's `code_hash` attribute.\n\nContract execution involves calling a function in the contract in a WASM virtual machine (Wasmtime).  This sandboxing isolates contract execution from the rest of the blockchain.  We first must get the executable code from account storage:\n\n```rust\nlet code = self\n    .accounts\n    .get_account(\u0026to)?\n    .code_hash\n    .ok_or_else(|| ChainError::NotAContractAccount(to.to_string()))?;\n```\n\nNow we just extract the function name and the function parameters from the `data` node in the transaction request:\n\n```rust \nlet (function, params): (\u0026str, Vec\u003c\u0026str\u003e) = bincode::deserialize(\u0026data)?;\n```\n\nFor example, let's say we want to invoke the `construct` function.  The function signature of `construct` contract function is:\n\n```rust\nfn construct(name: String, symbol: String) {}\n```\n\nWe serialize the parameter types and values:\n\n\n```rust\n// [\"Param 1 Type\", \"Param 1 Value\", \"Param 2 Type\", \"Param 2 Value\"]\nlet params = [\"String\", \"Rust Coin\", \"String\", \"RustCoin\"];\n```\n\nWe can now invoke the `construct` function:\n\n```rust\nruntime::contract::call_function(\u0026code, \"construct\", \u0026params)?;\n```\n\nAfter we've handled one of the 3 transaction types, the `from` account's `nonce` is updated.  A `transaction receipt` is created and returned from the function.\n\n## Organization\n\n### Chain\n\nThe [chain](chain) crate is a simplistic ethereum blockchain node.\nIt currently holds state in memory (TBD on disk storage).\nThe external json-rpc API mirrors that of Ethereum.\nIt contains a WASM runtime for executing contracts.\n\n#### Sample API: eth_blockNumber\n\n##### Request\n```shell\ncurl -X POST \\\n     -H 'Content-Type: application/json' \\\n     -d '{\"jsonrpc\":\"2.0\",\"id\":\"id\",\"method\":\"eth_accounts\",\"params\":[]}' \\ \n     http://127.0.0.1:8545\n```\n\n##### Response\n\n```json\n{\n    \"jsonrpc\":\"2.0\",\n    \"id\":\"id\",\n    \"result\":[\n        \"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266\",\n        \"0x70997970c51812dc3a010c7d01b50e0d17dc79c8\",\n        \"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc\"\n    ]\n}\n```\n\nThe full API can be found in the chain [README](chain).\n\n### Runtime\n\nThe [runtime](runtime) crate is a wasmtime runtime for executing WASM contracts.\nIt leverages the [component model](https://github.com/WebAssembly/component-model) and [wit-bindgen](https://github.com/bytecodealliance/wit-bindgen) to simplify host and guest interactions.\n\n### Contracts\n\nThe [contracts](contracts) directory holds the WASM source code.\nUsing [wit-bindgen](https://github.com/bytecodealliance/wit-bindgen), we can greatly simplify dealing with complex types.\n\n#### WIT\n\nThe [WIT format](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md) specifies a language for generating WASM code. \n\n```wit\ndefault world contract {\n  export erc20: interface {\n    construct: func(name: string, symbol: string)\n    mint: func(account: string, amount: u64)\n    transfer: func(to: string, amount: u64)\n  }\n}\n```\n\n#### Sample Contract - Erc20\n\nUsing the magical `generate!` macro, we remove boilerplate glue code, so all you see is the Rust contract. \n\n```rust\nuse wit_bindgen_guest_rust::*;\n\nwit_bindgen_guest_rust::generate!({path: \"../erc20/erc20.wit\", world: \"erc20\"});\n\nstruct Erc20;\n\nexport_contract!(Erc20);\n\nimpl erc20::Erc20 for Erc20 {\n    fn construct(name: String, symbol: String) {\n        println!(\"name {}, symbol\", symbol);\n    }\n\n    fn mint(account: String, amount: u64) {\n        println!(\"account {}, amount\", amount);\n    }\n\n    fn transfer(to: String, amount: u64) {\n        println!(\"to {}, amount\", amount);\n    }\n}\n```\n\n#### Invoking a Contract Function\n\nThis code can convert the textual representation of a contract function call to a function call within the wasmtime runtime.\nParameters are listed in pairs of parameter type and paramater value.\n\n```rust\nlet bytes = include_bytes!(\"./../../target/wasm32-unknown-unknown/release/erc20_wit.wasm\");\nlet function_name = \"construct\";\nlet params = \u0026[\"String\", \"Rust Coin\", \"String\", \"RustCoin\"];\n\ncall_function(bytes, function_name, params)?;\n```\n\n### Web3\n\nThe [web3](web3) crate is a naive implementation of a Web3 interface.\nIt has been minimized to focus on learning the concepts of the blockchain.\nThe goal will be to build out some of the most used endpoints.\n\n#### Sample Usage\n\n```rust\nuse web3::Web3;\n\nlet web3 = Web3::new(\"http://127.0.0.1:8545\")?;\nlet all_accounts = web3.get_all_accounts().await;\nlet balance = web3.get_balance(all_accounts[0]).await;\n\nlet block_number = web3.get_block_number().await?;\nlet block = web3.get_block(*block_number).await?;\n\nlet contract =\n    include_bytes!(\"./../../target/wasm32-unknown-unknown/release/erc20_wit.wasm\").to_vec();\nlet tx_hash = web3.deploy(all_accounts[0], \u0026contract).await?;\nlet receipt = web3.transaction_receipt(tx_hash).await?;\nlet code = web3.code(receipt.contract_address.unwrap(), None).await?;\n```\n\nMore information can be found in the web3 [README](web3).\n\n### Types\n\nThe [types](types) crate holds shared types to be used by the other crates.\n\n### Crypto\n\nThe [crypto](crypto) crate provides functions for generating keys, hashing data, signing and verifying.\n\n## Getting Started\n\nFirst, start the chain:\n\n```shell\ncd chain\nRUST_LOG=info cargo run\n```\n\nYou should see:\n\n```console\n2023-01-25T00:58:58.382776Z  INFO chain::server: Starting server on 127.0.0.1:8545\n```\n\nYou can now send [json-rpc calls](web3) to the API.\n\n## Compiling\n\n```rust\ncargo build\n```\n\n## Running Tests\n\n```rust\ncargo test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fddimaria%2Frust-blockchain-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fddimaria%2Frust-blockchain-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fddimaria%2Frust-blockchain-tutorial/lists"}