Tomasz Drwięga
@tomusdrw
25 May 2018 RustFest, Paris
André Silva
@andrebeat
Parity Technologies
Everything built with Rust (+ some JS).
Leave your e-mail:
Part 1: Blocks & Transactions
Part 2: Smart Contracts
Part 3: Rust Smart Contracts
List of changes
List of changes
List of changes
Metadata
/ Previous state
Metadata
/ Previous state
Metadata
/ Previous state
Genesis State
Blockchain
Immutable data structure containing all the changes that were applied in any point in time
(Boot) Node 1
Node 2
New Node
Hey! Could you give me all your peers?
(Boot) Node 1
Node 2
New Node
Hey! Send me all them blocks, will ya?
Block 5
Block 4
Block 0
(Boot) Node 1
Node 2
New Node
Hey! I've got a transaction to include in block.
Block 5
Block 5
Block 5
transfer(N, B, 5)
sig(N)
(Boot) Node 1
Node 2
New Node
Block 5
Block 5
Block 5
transfer(N, B, 5)
sig(N)
transfer(N, B, 5)
sig(N)
Cool, I'm mining and will include the tx for a small fee.
(Boot) Node 1
Node 2
New Node
Block 6
Block 5
Block 5
transfer(N, B, 5)
sig(N)
transfer(N, B, 5)
sig(N)
Block 6
Managed to mine new block, here it is guys!
(Boot) Node 1
Node 2
New Node
Block 6
Block 6
Block 6
List of changes
List of changes
List of changes
Metadata
/ Previous state
Metadata
/ Previous state
Metadata
/ Previous state
Genesis State
Blockchain
Immutable data structure containing all the changes that were applied in any point in time
Blockchain
Hashes - prevent tampering (e.g. KECCAK256)
Signatures - authorize the actions (e.g. ECDSA)
Parent = hash(B0) Timestamp = 150..000 Number = 1
Hash = hash(B1)
transfer(A, B, 5)
sig(A)
transfer(C, B, 1)
sig(C)
Parent = hash(B2) Timestamp = 150..000 Number = 2
Hash = hash(B1)
transfer(B, A, 5)
sig(B)
sig(Authority1)
hash(B0)
hash(B1)
sig(Authority2)
hash(B2)
sig(Authority1)
Proof of Authority
We only accept blocks signed by a hardcoded list of authorities.
Blocks need to be signed in turns at most 1 block per 3 seconds.
Difficulty=2 Sol.=0b001.. SolvedBy=A
hash(B0)
hash(B1)
hash(B2)
Proof of Work
We only accept blocks with a solution to a puzzle.
The difficulty of the puzzle can be adjusted to have a stable rate of new blocks.
Difficulty=4 Sol.=0b00001.. SolvedBy=B
Difficulty=3 Sol.=0b0001.. SolvedBy=A
Canonical Chain
What if two different blocks are produced with the same parent hash?
Which one should you choose?
Block 1
Block 2
Block 3
Block 3
Fork
Canonical Chain
We use "the longest" chain.
Ethereum re-organizes to a chain with the highest difficulty.
Block 1
Block 2
Block 3
Block 3
Block 4
Take away note: The latest state you see can sometimes be reverted - wait for confirmations.
Blockchains allow for trustless transactions between multiple parties.
$ parity ui --chain=dev --mode=offline --ws-origins=all
# or
$ parity ui --chain=dev --network-id=<random-number> --ws-origins=all
$ cd ~/.local/share/io.parity.ethereum/dapps
# ~/Library/Application Support/io.parity.ethereum/dapps
# %AppData%\Parity\Ethereum\dapps
$ git clone --depth=1 -b built https://github.com/tomusdrw/etherdisplay
{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_getBlockByNumber",
"params": ["latest", false]
}
# apt install httpie
$ http localhost:8545 jsonrpc=2.0 id=1 method=eth_getBlockByNumber params:='["latest",false]'
# Or with CURL
$ curl localhost:8545 -H "Content-Type:application/json" -X POST --data \
'{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByNumber","params":["latest",false]}'
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"author": "0x05a56e2d52c817161883f50c441c3228cfe54d9f",
"difficulty": "0x3ff800000",
"extraData": "0x476574682f76312e302e302f6c696e75782f676f312e342e32",
"gasLimit": "0x1388",
"gasUsed": "0x0",
"hash": "0x88e96d4537bea4d9c05d12549907b32561d3bf31f45aae734cdc119f13406cb6",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x05a56e2d52c817161883f50c441c3228cfe54d9f",
"mixHash": "0x969b900de27b6ac6a67742365dd65f55a0526c41fd18e1b16f1a1215c2e66f59",
"nonce": "0x539bd4979fef1ec4",
"number": "0x1",
"parentHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sealFields": [
"0xa0969b900de27b6ac6a67742365dd65f55a0526c41fd18e1b16f1a1215c2e66f59",
"0x88539bd4979fef1ec4"
],
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x219",
"stateRoot": "0xd67e4d450343046425ae4271474353857ab860dbc0a1dde64b41b5cd3a532bf3",
"timestamp": "0x55ba4224",
"totalDifficulty": "0x7ff800000",
"transactions": [],
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncles": []
}
}
Documentation of all methods:
https://github.com/paritytech/parity/wiki/JSONRPC-eth-module
// Returns a block data
// and transactions in it
eth_getBlockByNumber(
<block-number>,
<include-transactions>,
);
// Returns a balance of an account.
eth_getBalance(
<address>,
);
Setup and experiment with the parity client
IN RUST!
extern crate web3;
use web3::futures::Future;
use web3::types::BlockNumber;
fn main() {
// Initialize the transport (can use Ipc transport as well)
let (_eloop, transport) = web3::transports::Http::new(
"http://localhost:8545",
).unwrap();
let web3 = web3::Web3::new(transport);
// Invoke the RPC method and deserialize the result
let block = web3.eth().block_with_txs(
BlockNumber::Latest.into(),
).wait().unwrap();
// Print the result.
println!("Latest block: {:?}", block);
}
extern crate web3;
use web3::futures::Future;
fn main() {
// Initialize the transport (can use Ipc transport as well)
let (_eloop, transport) = web3::transports::Http::new(
"http://localhost:8545",
).unwrap();
let web3 = web3::Web3::new(transport);
// Invoke the RPC method and deserialize the result
let address = "0x00a329c0648769a73afac7f9381e08fb43dbea72".parse().unwrap();
let nonce = web3.eth().transaction_count(address, None).wait().unwrap();
// Print the result.
println!("Number of transactions sent from {:?}: {:?}", address, nonce);
}
Notify yourself whenever a balance of some account changes.
https://github.com/tomusdrw/rustfest-2017
Smart Contract is like an agent behind a regular address.
pragma solidity ^0.4.11;
contract Burn {
uint256 public value;
address public owner;
function Burn() public payable {
value = msg.value;
owner = msg.sender;
}
}
pragma solidity ^0.4.11;
contract Parity {
uint256 public value;
address public owner;
function export() payable {
value += msg.value;
owner = msg.sender;
}
}
0xc0de15de4d... at 0x123..456
binary at /usr/bin/parity
$ parity export blocks
from: 0x456..def
to: 0x123..456
value: 5 * 10^18 wei (5 ETH)
gas: 100,000
gasPrice: 4 * 10^9 wei (4 shannon)
nonce: 0
data: 0x444...
("call function export")
0x123456... (Transaction RLP)
pragma solidity ^0.4.17;
contract Lock {
uint256 public value;
address public owner;
function Lock() public payable {
value = msg.value;
owner = msg.sender;
}
function withdraw() public {
require(msg.sender == owner);
msg.sender.transfer(value);
}
}
pragma solidity ^0.4.17;
contract Lock {
struct Balance {
uint256 value;
}
mapping(address => Balance) public locked;
function lock() public payable {
locked[msg.sender] = Balance(msg.value);
}
function unlock() public {
var balance = locked[msg.sender];
require(balance.value != 0);
msg.sender.transfer(balance.value);
delete locked[msg.sender];
}
}
Can you spot a bug in this contract?
extern crate web3;
use web3::futures::Future;
use web3::contract::{Contract, Options};
use web3::types::{Address, U256};
fn main() {
let (_eloop, http) = web3::transports::Http::new(
"http://localhost:8545"
).unwrap();
let web3 = web3::Web3::new(http);
// The contract address.
let address: Address = "0x00a329c0648769a73afac7f9381e08fb43dbea72"
.parse().unwrap();
// Access the contract
let contract = Contract::from_json(web3.eth(), address, include_bytes!("./abi.json")).unwrap();
// Query the contract instance
let result = contract.query("locked", (address, ), None, Options::default(), None);
let balance_of: U256 = result.wait().unwrap();
assert_eq!(balance_of, 0.into());
}
extern crate web3;
extern crate rustc_hex;
use web3::futures::Future;
use web3::contract::{Contract, Options};
use web3::types::{Address, U256};
use rustc_hex::FromHex;
fn main() {
let (_eloop, http) = web3::transports::Http::new("http://localhost:8545").unwrap();
let web3 = web3::Web3::new(http);
// The account you deploy from.
let my_account: Address = "0x00a329c0648769a73afac7f9381e08fb43dbea72".parse().unwrap();
// Get the contract bytecode for instance from Solidity compiler.
let bytecode = include_str!("./bytecode.bin").from_hex().unwrap();
// Deploying a contract
let contract = Contract::deploy(web3.eth(), include_bytes!("./abi.json")).unwrap()
.confirmations(0)
.options(Options::with(|mut opt| opt.value = Some(5.into())))
.execute(bytecode, (), my_account)
.expect("Correct parameters are passed to the constructor.")
.wait()
.unwrap();
// Query the contract instance
let result = contract.query("locked", (my_account, ), None, Options::default(), None);
let balance_of: U256 = result.wait().unwrap();
assert_eq!(balance_of, 0.into());
}
extern crate hyper;
extern crate web3;
use web3::futures::{self, Future};
use hyper::header::ContentLength;
use hyper::server::{Http, Request, Response, Service};
struct HelloWorld;
impl Service for HelloWorld {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = Box<Future<Item=Self::Response, Error=Self::Error>>;
fn call(&self, _req: Request) -> Self::Future {
let phrase = "Hello World!";
Box::new(futures::future::ok(
Response::new()
.with_header(ContentLength(phrase.len() as u64))
.with_body(phrase)
))
}
}
fn main() {
let addr = "127.0.0.1:3000".parse().unwrap();
let server = Http::new().bind(&addr, || Ok(HelloWorld)).unwrap();
server.run().unwrap();
}
extern crate hyper;
extern crate web3;
use std::sync::Arc;
use hyper::header::ContentLength;
use hyper::server::{Http, Request, Response, Service};
use web3::futures::Future;
use web3::types::Address;
#[derive(Clone)]
struct HelloBlockchain { web3: Arc<web3::Web3<web3::transports::Http>> }
impl Service for HelloBlockchain {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = Box<Future<Item=Self::Response, Error=Self::Error>>;
fn call(&self, _req: Request) -> Self::Future {
let address: Address = "0x00a329c0648769a73afac7f9381e08fb43dbea72".parse().unwrap();
Box::new(self.web3.eth().balance(address, None)
.then(|balance| {
let body = format!("Balance: {:?}", balance);
Ok(Response::new()
.with_header(ContentLength(body.len() as u64))
.with_body(body))
}))
}
}
fn main() {
let (_eloop, http) = web3::transports::Http::new("http://localhost:8545").unwrap();
let web3 = Arc::new(web3::Web3::new(http));
let addr = "127.0.0.1:3000".parse().unwrap();
let handler = HelloBlockchain { web3 };
let server = Http::new().bind(&addr, move || Ok(handler.clone())).unwrap();
server.run().unwrap();
}
Create a WebServer and limit the access to it only for users who payed.
https://github.com/tomusdrw/rustfest-2017
IN RUST!