├── .gitignore ├── identities.js ├── package.json ├── genesis.json ├── solidity ├── FreeBeer_sol_FreeBeer.abi ├── FreeBeer_sol_FreeBeer.bin ├── FreeBeer.sol └── FreeBeer.sol.js ├── alice └── keystore │ └── UTC--2017-08-25T03-13-24.087502890Z--dda6ef2ff259928c561b2d30f0cad2c2736ce8b6 ├── bob └── keystore │ └── UTC--2017-08-25T03-14-55.548430434Z--8691bf25ce4a56b15c1f99c944dc948269031801 ├── lily └── keystore │ └── UTC--2017-08-25T20-32-28.006040719Z--b1b6a66a410edc72473d92decb3772bad863e243 ├── LICENSE ├── eth-private-net └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | history 3 | geth 4 | node_modules 5 | package-lock.json 6 | -------------------------------------------------------------------------------- /identities.js: -------------------------------------------------------------------------------- 1 | alice = "0xdda6ef2ff259928c561b2d30f0cad2c2736ce8b6" 2 | bob = "0x8691bf25ce4a56b15c1f99c944dc948269031801" 3 | lily = "0xb1b6a66a410edc72473d92decb3772bad863e243" 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eth-private-net", 3 | "version": "1.0.0", 4 | "description": "Get your own personal Ethereum private net.", 5 | "homepage": "https://github.com/vincentchu/eth-private-net", 6 | "dependencies": { 7 | "solc": "^0.4.16" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/vincentchu/eth-private-net" 12 | }, 13 | "author": "Vincent Chu" 14 | } 15 | -------------------------------------------------------------------------------- /genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "chainId": 15, 4 | "homesteadBlock": 0, 5 | "eip155Block": 0, 6 | "eip158Block": 0 7 | }, 8 | "difficulty": "400000", 9 | "gasLimit": "2100000", 10 | "alloc": { 11 | "dda6ef2ff259928c561b2d30f0cad2c2736ce8b6": { "balance": "1000000000000000000" }, 12 | "8691bf25ce4a56b15c1f99c944dc948269031801": { "balance": "1000000000000000000" } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /solidity/FreeBeer_sol_FreeBeer.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[],"name":"gimmeMoney","outputs":[{"name":"","type":"bool"}],"payable":true,"stateMutability":"payable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"MoneySent","type":"event"}] -------------------------------------------------------------------------------- /alice/keystore/UTC--2017-08-25T03-13-24.087502890Z--dda6ef2ff259928c561b2d30f0cad2c2736ce8b6: -------------------------------------------------------------------------------- 1 | {"address":"dda6ef2ff259928c561b2d30f0cad2c2736ce8b6","crypto":{"cipher":"aes-128-ctr","ciphertext":"28d2b8579cf9ac60634a11f048f341d3faf22daaed79804ece0118988528e90a","cipherparams":{"iv":"f519fa5fd40a0fe4f74a19311ac40209"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c0c3ed7a2b30ab5efb2ed5634e1e45c1f5769985beb701e328f9d90513926561"},"mac":"115456abbbea9bf1cc765f2ec74abb8103202ff22fc0463298d9a39d59d38bc4"},"id":"d038b439-5c96-4290-9e8d-a3e034c652a1","version":3} -------------------------------------------------------------------------------- /bob/keystore/UTC--2017-08-25T03-14-55.548430434Z--8691bf25ce4a56b15c1f99c944dc948269031801: -------------------------------------------------------------------------------- 1 | {"address":"8691bf25ce4a56b15c1f99c944dc948269031801","crypto":{"cipher":"aes-128-ctr","ciphertext":"cbffbd787334d0d02f82d8a755d3febf5da10e53ce2198bab223b8a84a610683","cipherparams":{"iv":"7c941590afc4d6228d16cb519639e0cd"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"0ed052a6f0027a49cee17665365fd9af15be6782aec75011e80529272b86d3c2"},"mac":"53fa692d24bd794ac5ee51f673041faeae70dd471bc942519382b364b95ff236"},"id":"de9c7579-1912-4439-acc6-bb51610c1e9b","version":3} -------------------------------------------------------------------------------- /lily/keystore/UTC--2017-08-25T20-32-28.006040719Z--b1b6a66a410edc72473d92decb3772bad863e243: -------------------------------------------------------------------------------- 1 | {"address":"b1b6a66a410edc72473d92decb3772bad863e243","crypto":{"cipher":"aes-128-ctr","ciphertext":"952144e89bfe9314f563a92654a5f0e6d12031c4aa2e8652759305b65fa60429","cipherparams":{"iv":"7d65bf9cba38a65cbf3c21a6a55de286"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"6f1fb36beaed141df45f375db0c368ebeb3f35b8dd637f1dde1b851e1d476e88"},"mac":"53db69c65201990a99b4c230a12f2ca09a247e09a2bc60a695173e259d952964"},"id":"e69333ec-fbaf-414a-ad6b-c9601f388537","version":3} -------------------------------------------------------------------------------- /solidity/FreeBeer_sol_FreeBeer.bin: -------------------------------------------------------------------------------- 1 | 6060604052341561000f57600080fd5b5b60008054600160a060020a03191633600160a060020a03161790555b5b6101558061003c6000396000f3006060604052361561003e5763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663944d1ea1811461004b575b5b610047610067565b505b005b610053610067565b604051901515815260200160405180910390f35b6000805473ffffffffffffffffffffffffffffffffffffffff163480156108fc0290604051600060405180830381858888f1935050505015610122576000547fbc8f5a5cb90bc33d83a08f0663beb02c65e4c51522ef0c8230d36370088a0a1990339073ffffffffffffffffffffffffffffffffffffffff163460405173ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604080820192909252606001905180910390a1506001610126565b5060005b905600a165627a7a723058203dc9952621b6fb093a9c28af6f0e086c9794dd6f8a60a5b27aa463833ffefc9f0029 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Vincent Chu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /solidity/FreeBeer.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | 3 | // FreeBeer 4 | // 5 | // This is a simple, HelloWorld style contract that allows anybody on the Ethereum 6 | // network to send the contract owner some money. It illustrates: 7 | // 8 | // - Contract creation 9 | // - Use of a payable method to transfer value 10 | // - Event emission 11 | contract FreeBeer { 12 | 13 | // Each time money is transferred, emit the following Event to track who sender and value sent 14 | event MoneySent(address sender, address recipient, uint256 amount); 15 | 16 | // Declare the recipient of the contract 17 | address recipient; 18 | 19 | // The contract's constructor. This is called once (and only once) on contract creation. All it 20 | // does is set the contract's recipient to the creator of the contract. 21 | function FreeBeer() { 22 | recipient = msg.sender; 23 | } 24 | 25 | // The contract's only usable method. It's marked with the payable keyword so it can accept 26 | // value when called. Sends whatever value is included with the calling transaction to the 27 | // owner of the contract (i.e., the recipient). If successful, emits a MoneySent event, 28 | // and returns true. Returns false if transaction fails. 29 | function gimmeMoney() payable returns (bool) { 30 | if (recipient.send(msg.value)) { 31 | MoneySent(msg.sender, recipient, msg.value); 32 | return true; 33 | } 34 | 35 | return false; 36 | } 37 | 38 | // The contract's fallback function. This is a catch-all function that is executed whenever a 39 | // contract is called and no public methods match, or no method was specified. In this case, just 40 | // call .gimmeMoney() to send the contract owner whatever money is in the transaction. 41 | function () payable { 42 | gimmeMoney(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /solidity/FreeBeer.sol.js: -------------------------------------------------------------------------------- 1 | // Compiled bytecode for FreeBeer (using --bin) 2 | var freeBeerBytecode = "0x6060604052341561000f57600080fd5b5b60008054600160a060020a03191633600160a060020a03161790555b5b6101558061003c6000396000f3006060604052361561003e5763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663944d1ea1811461004b575b5b610047610067565b505b005b610053610067565b604051901515815260200160405180910390f35b6000805473ffffffffffffffffffffffffffffffffffffffff163480156108fc0290604051600060405180830381858888f1935050505015610122576000547fbc8f5a5cb90bc33d83a08f0663beb02c65e4c51522ef0c8230d36370088a0a1990339073ffffffffffffffffffffffffffffffffffffffff163460405173ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604080820192909252606001905180910390a1506001610126565b5060005b905600a165627a7a723058203dc9952621b6fb093a9c28af6f0e086c9794dd6f8a60a5b27aa463833ffefc9f0029" 3 | 4 | // Application Binary Interface (ABI) for FreeBeer (using --abi) 5 | var freeBeerAbi = [ 6 | { 7 | "constant": false, 8 | "inputs": [], 9 | "name": "gimmeMoney", 10 | "outputs": [ 11 | { 12 | "name": "", 13 | "type": "bool" 14 | } 15 | ], 16 | "payable": true, 17 | "stateMutability": "payable", 18 | "type": "function" 19 | }, 20 | { 21 | "inputs": [], 22 | "payable": false, 23 | "stateMutability": "nonpayable", 24 | "type": "constructor" 25 | }, 26 | { 27 | "payable": true, 28 | "stateMutability": "payable", 29 | "type": "fallback" 30 | }, 31 | { 32 | "anonymous": false, 33 | "inputs": [ 34 | { 35 | "indexed": false, 36 | "name": "sender", 37 | "type": "address" 38 | }, 39 | { 40 | "indexed": false, 41 | "name": "recipient", 42 | "type": "address" 43 | }, 44 | { 45 | "indexed": false, 46 | "name": "amount", 47 | "type": "uint256" 48 | } 49 | ], 50 | "name": "MoneySent", 51 | "type": "event" 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /eth-private-net: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | IDENTITIES=(alice bob lily) 4 | FLAGS='--networkid=8888 --preload=identities.js' 5 | DEV_FLAGS='--nodiscover --verbosity=4' 6 | 7 | BASE_PORT=40300 8 | BASE_RPC_PORT=8100 9 | 10 | SCRIPT=$(basename "$0") 11 | 12 | USAGE="Name: 13 | $SCRIPT - Command line utility for creating and running a three node Ethereum private net 14 | 15 | Network comes with three identities at the following addresses: 16 | 17 | alice: 0xdda6ef2ff259928c561b2d30f0cad2c2736ce8b6 (initialized with 1 Ether) 18 | bob: 0x8691bf25ce4a56b15c1f99c944dc948269031801 (initialized with 1 Ether) 19 | lily: 0xb1b6a66a410edc72473d92decb3772bad863e243 20 | 21 | Usage: 22 | $SCRIPT command [command options] 23 | 24 | Commands: 25 | init Initialize private network from genesis block in genesis.json 26 | clean Destroy all blockchain history, resetting to pristine state 27 | start Start a running ethereum node on the network (example: 'start alice') 28 | connect Connect two running nodes as peers (example: 'connect alice bob') 29 | help Print this help message 30 | 31 | Author: 32 | Vincent Chu (@vincentchu), Initialized Capital 33 | https://github.com/vincentchu/eth-private-net 34 | " 35 | 36 | function exec_on_node() { 37 | geth --exec="$2" attach ./$1/geth.ipc 38 | } 39 | 40 | CMD=$1 41 | case $CMD in 42 | init) 43 | for IDENTITY in ${IDENTITIES[@]}; do 44 | echo "Initializing genesis block for $IDENTITY" 45 | geth --datadir=./$IDENTITY $FLAGS init genesis.json 46 | done 47 | ;; 48 | 49 | clean) 50 | for IDENTITY in ${IDENTITIES[@]}; do 51 | echo "Cleaning geth/ directory from $IDENTITY" 52 | rm -r ./$IDENTITY/geth 53 | done 54 | ;; 55 | 56 | start) 57 | IDENTITY=$2 58 | if [ -z $IDENTITY ] 59 | then 60 | echo "No identity specified. Identity must be one of: ${IDENTITIES[*]}" 61 | exit -1 62 | fi 63 | 64 | case $IDENTITY in 65 | alice) 66 | OFFSET=1 67 | ;; 68 | 69 | bob) 70 | OFFSET=2 71 | ;; 72 | 73 | lily) 74 | OFFSET=3 75 | ;; 76 | esac 77 | 78 | PORT=$((BASE_PORT + OFFSET)) 79 | RPC_PORT=$((BASE_RPC_PORT + OFFSET)) 80 | 81 | echo "Starting node for $IDENTITY on port: $PORT, RPC port: $RPC_PORT. Console logs sent to ./$IDENTITY/console.log" 82 | geth --datadir=./$IDENTITY --port=$PORT --rpcport=$RPC_PORT $FLAGS $DEV_FLAGS console 2>> ./$IDENTITY/console.log 83 | ;; 84 | 85 | connect) 86 | IDENTITY1=$2 87 | IDENTITY2=$3 88 | 89 | ENODE=$(exec_on_node $IDENTITY1 'admin.nodeInfo.enode') 90 | CONNECT_JS="admin.addPeer($ENODE)" 91 | 92 | exec_on_node $IDENTITY2 $CONNECT_JS 93 | ;; 94 | 95 | help) 96 | echo "$USAGE" 97 | ;; 98 | 99 | *) 100 | echo "Command '$CMD' not recognized!" 101 | echo "$USAGE" 102 | exit -1 103 | ;; 104 | esac 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eth-private-net 2 | 3 | `eth-private-net` is a simple tool that allows you to quickly setup a three-node private Ethereum network running locally on your personal computer. The tool makes initializing, starting, and connecting nodes fast and easy. The network comes with three pre-made identities (Alice, Bob, and Lily) and a tutorial that walks you through simple actions like mining and transferring Ether, and culminates with the deployment and execution of a simple smart contract called `FreeBeer`. 4 | 5 | I wrote this tool because I found a lot of the Ethereum tooling opaque and difficult to understand. A lot of the information that I needed to know was scattered through the documentation, and there wasn't a single place that wrapped together the most instructive examples to build my understanding. Moreover, I wanted a tool that would allow me to repeatedly reinitialize a network from a known, clean state to help me experiment with building, deploying, and running smart contracts. 6 | 7 | **Prerequisites:** Make sure `geth` is installed and in the `$PATH`. `geth` is a golang implementation of the Ethereum protocol and provides command line tools for interacting with the Ethereum network. You can download [pre-compiled binaries](https://ethereum.github.io/go-ethereum/downloads/) or install from Homebrew or source using the [installation instructions](https://www.ethereum.org/cli). 8 | 9 | The base denomination of currency on the Ethereum network is the [Ether](https://en.wikipedia.org/wiki/Ethereum#Ether). However, since most actions on the network require just fractions of an Ether, we'll be referring to various denominations of Ether throughout the tutorial (e.g., Wei, GWei, Szabo, etc.). This [site](https://etherconverter.online/) gives an overview of the various denominations, and allows you to convert between them. 10 | 11 | ## Identities 12 | 13 | The private network comes with three identities (each secured with `foobar123` as the password): 14 | 15 | - Alice: `0xdda6ef2ff259928c561b2d30f0cad2c2736ce8b6` 16 | - Bob: `0x8691bf25ce4a56b15c1f99c944dc948269031801` 17 | - Lily: `0xb1b6a66a410edc72473d92decb3772bad863e243` 18 | 19 | Each user's identity is stored in `./[NAME]/keystore/UTC-...`. 20 | 21 | ## Initializing From Genesis Block 22 | 23 | Since we're bootstrapping our own private chain, we'll need a [genesis block](https://github.com/ethereum/go-ethereum/wiki/Private-network#creating-the-genesis-block). The definition for our block will be stored in [`genesis.json`](https://github.com/vincentchu/eth-private-net/blob/master/genesis.json). Both Alice and Bob's addresses are pre-allocated with 1 Ether (or 1e+18 [Wei](http://ethdocs.org/en/latest/ether.html)). Just run: 24 | 25 | ``` 26 | → ./eth-private-net init 27 | ``` 28 | 29 | **Note:** Tearing down your private network and resetting all account balances is easy. Just run: 30 | 31 | ``` 32 | → ./eth-private-net clean 33 | ``` 34 | 35 | ## Running a Private Test Net 36 | 37 | Nodes for `alice`, `bob`, or `lily` can be started easily with the `start` action of the convenience script: 38 | 39 | ``` 40 | → ./eth-private-net start alice 41 | Starting node for alice on port: 40301, RPC port: 8101. Console logs sent to ./alice/console.log 42 | Welcome to the Geth JavaScript console! 43 | ``` 44 | 45 | This starts a running Ethereum node, loads whatever identity you specified on the command line, and starts a console where you can begin interacting with your private net. The console itself is a Javascript REPL with all of the commands you need to begin working with Ethereum preloaded. You can check out all of the available commands [here](https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console#management-api-reference). 46 | 47 | The first thing you can do is check Alice's balance, which should show exactly 1e+18 Wei (1 Ether). 48 | 49 | ``` 50 | # As alice: 51 | 52 | > eth.getBalance("0xdda6ef2ff259928c561b2d30f0cad2c2736ce8b6") 53 | 1000000000000000000 54 | ``` 55 | 56 | For convenience, the addresses for alice, bob, and lily have been aliased to variables (see: [`identities.js`](https://github.com/vincentchu/eth-private-net/blob/master/identities.js)) allowing you to use them quickly and easily: 57 | 58 | ``` 59 | > bob 60 | "0x8691bf25ce4a56b15c1f99c944dc948269031801" 61 | 62 | > [ eth.getBalance(alice), eth.getBalance(bob), eth.getBalance(lily) ] 63 | [1000000000000000000, 1000000000000000000, 0] 64 | ``` 65 | 66 | You can also determine the Alice's [`enode`](https://github.com/ethereum/wiki/wiki/enode-url-format), a unique identifier for her node on the private network: 67 | 68 | ``` 69 | # As alice: 70 | 71 | > admin.nodeInfo.enode 72 | "enode://f15b1...@[::]:40301?discport=0" 73 | ``` 74 | 75 | Take this identifier and use it to connect Bob's node to Alice (make sure to start Bob's node with: `./eth-private-net start bob`): 76 | 77 | ``` 78 | # As bob: 79 | 80 | > admin.peers 81 | [] 82 | 83 | admin.addPeer("enode://f15b1...@[::]:40301?discport=0") 84 | 85 | > admin.peers 86 | [{ 87 | caps: ["eth/63"], 88 | id: "f15b12b1293d578b3ff35075d742f33bb7fe9c7357c309ba711bbae68a0263dcbde30ecdc4597dba100ad4f4ad353edc18198101b993ecf4188ca2c42a1443ee", 89 | name: "Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3", 90 | network: { 91 | localAddress: "[::1]:60273", 92 | remoteAddress: "[::1]:40301" 93 | }, 94 | protocols: { 95 | eth: { 96 | difficulty: 400000, 97 | head: "0xa528aeb0f22594c4ff224d87833cdbfbb6b0818b7892356ec1dea9a02c08b398", 98 | version: 63 99 | } 100 | } 101 | }] 102 | ``` 103 | 104 | The convenience method `./eth-private-net connect` allows you to connect two running nodes together. For instance, the following will create a three node net between Alice, Bob, and Lily: 105 | 106 | ``` 107 | → ./eth-private-net connect alice bob 108 | true 109 | 110 | → ./eth-private-net connect alice lily 111 | true 112 | 113 | → ./eth-private-net connect bob lily 114 | true 115 | ``` 116 | 117 | ## Mining 118 | 119 | The console allows you to begin mining on our private network easily. Simply execute `miner.start()`: 120 | 121 | ``` 122 | # As alice: 123 | 124 | > miner.start() 125 | null 126 | 127 | > eth.getBalance(eth.coinbase) 128 | 6000000000000000000 129 | 130 | > miner.stop() 131 | true 132 | 133 | > eth.blockNumber 134 | 1 135 | ``` 136 | 137 | **Note:** The first time you begin to mine, you'll need to generate a 1GB [Directed Acyclic Graph (DAG)](https://github.com/ethereum/wiki/wiki/Ethash-DAG). This dataset is used as part of Ethereum's Proof-of-Work system, Ethash, and is stored in `~/.ethash/`. This will take about a minute and you'll see the following lines in your node's `console.log`: 138 | 139 | ``` 140 | INFO [08-26|16:07:23] Generating DAG in progress epoch=0 percentage=0 elapsed=304.599ms 141 | INFO [08-26|16:07:23] Generating DAG in progress epoch=0 percentage=1 elapsed=550.655ms 142 | INFO [08-26|16:07:23] Generating DAG in progress epoch=0 percentage=2 elapsed=798.006ms 143 | ``` 144 | 145 | After a single block is mined, the balance of the account (or `eth.coinbase`) should increase by 5 ether. The current `blockNum` should then increase exactly by 1. Evidence of the mining should be present in the node's logfile (in this case, `alice/console.log`): 146 | 147 | ``` 148 | INFO [08-25|16:20:45] Starting mining operation 149 | INFO [08-25|16:20:45] Commit new mining work number=6 txs=0 uncles=0 elapsed=389.815µs 150 | DEBUG[08-25|16:20:45] Loaded old ethash dataset from disk epoch=0 151 | DEBUG[08-25|16:20:45] Loaded old ethash dataset from disk epoch=1 152 | INFO [08-25|16:20:53] Successfully sealed new block number=6 hash=3ed256…3ef7c2 153 | DEBUG[08-25|16:20:53] Trie cache stats after commit misses=15 unloads=0 154 | INFO [08-25|16:20:53] 🔨 mined potential block number=6 hash=3ed256…3ef7c2 155 | INFO [08-25|16:20:53] Commit new mining work number=7 txs=0 uncles=0 elapsed=444.679µs 156 | ``` 157 | 158 | ## Transferring Ether 159 | 160 | Now that we've mined a few blocks, let's try transferring some Ethereum. Let's start from a clean network. Shutdown any running nodes by typing `exit` at the console prompt. Clean and reinitialize the network by executing: 161 | 162 | ``` 163 | → ./eth-private-net clean 164 | 165 | → ./eth-private-net init 166 | ``` 167 | 168 | Start nodes for alice, bob, and lily (using `eth-private-net start [alice | bob | lily]`), and connect the three nodes using: 169 | 170 | ``` 171 | → ./eth-private-net connect alice bob 172 | 173 | → ./eth-private-net connect alice lily 174 | 175 | → ./eth-private-net connect bob lily 176 | ``` 177 | 178 | **Note:** The following examples assume that Bob is mining (to ensure transactions are processed); start his miner with `miner.start()`. 179 | 180 | Let's say Alice wants to transfer 1 Szabo (defined as 1 µEth or 1e+12 Wei) to Lily. We can have Alice send Lily some Ethereum by unlocking her account (with `foobar123` as the password), then sending a transaction with `.sendTransaction(...)`: 181 | 182 | ``` 183 | # As alice: 184 | 185 | > personal.unlockAccount(eth.coinbase) 186 | Unlock account 0xdda6ef2ff259928c561b2d30f0cad2c2736ce8b6 187 | Passphrase: 188 | true 189 | 190 | > txn = eth.sendTransaction({ from: alice, to: lily, value: web3.toWei(1, "szabo") }) 191 | "0xb0fa9985cd6549258d6d96823d24398ba339f7f555fa0a58ca4b980bbbbebfe5" 192 | ``` 193 | 194 | After the transaction has been processed, our account balances are now: 195 | 196 | ``` 197 | > [ eth.getBalance(alice), eth.getBalance(bob), eth.getBalance(lily) ] 198 | [999621000000000000, 31000378000000000000, 1000000000000] 199 | ``` 200 | 201 | We see that Lily now has 1 Szabo, as expected. However, Alice's new balance contains _less_ ether than we'd expect from the transaction. The difference is the _transaction fee_ that arises from the transfer and is given to Bob, the miner, as an incentive for processing transactions. Each transfer costs a fixed 21,000 gas. To convert this into Wei, we can run use `eth.gasPrice` to find the current cost of gas: 202 | 203 | ``` 204 | > eth.gasPrice 205 | 18000000000 206 | ``` 207 | 208 | Therefore, the transaction fee was `gas x gasPrice = 2.1e+04 x 1.8e+10 = 3.78e+14 Wei`. This accounts for the discrepancy in Alice's account (and the additional 3.78e+14 Wei that appears in Bob's account). In general, the gas used for executing transactions on the network (e.g., transferring ether, deploying a smart contract, or calling one) can be found with `eth.getTransactionReceipt`: 209 | 210 | ``` 211 | > eth.getTransactionReceipt("0xb0fa9985cd6549258d6d96823d24398ba339f7f555fa0a58ca4b980bbbbebfe5").cumulativeGasUsed 212 | 21000 213 | ``` 214 | 215 | A good metaphor for gas and gas price is electricity. In this metaphor, gas is equivalent to the amount of electricity, in kilowatt-hours (kW-h), used by various appliances in your house; gas price is then equivalent to the dollar cost of a kW-h charged by your utility. In the same way that running a lightbulb for an hour costs a fixed amount of kW-h (dictated by the physical characteristics of the bulb), an ether transfer costs a fixed amount of gas, regardless of the prevailing cost of electricity or gas. 216 | 217 | ## Deploying and Running Smart Contracts 218 | 219 | One of the most interesting features of the Ethereum blockchain is the ability to deploy and run [smart contracts](https://en.wikipedia.org/wiki/Smart_contract) on the blockchain. In this section, we'll go through a simple example of writing, deploying, and running a smart contract called `FreeBeer`. 220 | 221 | ### FreeBeer - A simple, HelloWorld-esque Smart Contract 222 | 223 | I've included a sample contract called `FreeBeer` in [`solidity/FreeBeer.sol`](https://github.com/vincentchu/eth-private-net/blob/master/solidity/FreeBeer.sol). The contract itself is simple and written in [Solidity](https://en.wikipedia.org/wiki/Solidity), a statically-typed language for writing smart contracts. It allows anybody on the Ethereum network to send the contract holder some money. Though it is simple, it illustrates some basic concepts around using smart contracts to send Ether. 224 | 225 | To deploy a Solidity contract, you'll need to compile it into an [Application Binary Interface (ABI)](https://github.com/vincentchu/eth-private-net/blob/master/solidity/FreeBeer_sol_FreeBeer.abi) and Ethereum Virtual Machine (EVM) [bytecode](https://github.com/vincentchu/eth-private-net/blob/master/solidity/FreeBeer_sol_FreeBeer.bin). The ABI itself is a bit of JSON that defines the contract's interface--- e.g., what methods it exposes or the types of its arguments. The bytecode is a hex-encoded string that allows the contract to be run on the Ethereum Virtual Machine (EVM) when the contract is called. 226 | 227 | **Note:** Installing `solc-js` isn't strictly necessary, but you'll need it if you want to play around with your own smart contracts. Running `npm install` installs the executable under `./node_modules/.bin/solcjs`. 228 | 229 | I've pre-compiled FreeBeer's ABI and bytecode (both in `solidity/`) using [`sol-js`](https://github.com/ethereum/solc-js) and wrapped both in a simple javascript file that allows easy use inside the geth console: 230 | 231 | ``` 232 | # As alice: 233 | 234 | > loadScript('solidity/FreeBeer.sol.js') 235 | true 236 | 237 | > freeBeerBytecode 238 | "0x6060604052341561000f57600080fd5b5b60008054600160a060020a03191633600160a060020a03161790555b5b6101558061003c6000396000f3006060604052361561003e5763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663944d1ea1811461004b575b5b610047610067565b505b005b610053610067565b604051901515815260200160405180910390f35b6000805473ffffffffffffffffffffffffffffffffffffffff163480156108fc0290604051600060405180830381858888f1935050505015610122576000547fbc8f5a5cb90bc33d83a08f0663beb02c65e4c51522ef0c8230d36370088a0a1990339073ffffffffffffffffffffffffffffffffffffffff163460405173ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604080820192909252606001905180910390a1506001610126565b5060005b905600a165627a7a723058203dc9952621b6fb093a9c28af6f0e086c9794dd6f8a60a5b27aa463833ffefc9f0029" 239 | ``` 240 | 241 | ### Deploying 242 | 243 | Note: Make sure you unlock the accounts before deploying a contract or executing calls against it. You can do so by running `personal.unlockAccount(...)` and using `foobar123` as the password. Also **make sure that a miner is running** (in these examples, Lily is the sole running miner). 244 | 245 | Suppose Alice wishes to deploy `FreeBeer` to allow anybody to send her some Ether. First, she'll need to prepare a transaction specifying the contract's compiled bytecode as data. We'll also provide 200,000 gas to pay for the deployment, and use `eth.estimateGas(...)` to check that our supplied gas is sufficient to pay for the contract's deployment: 246 | 247 | ``` 248 | # As alice: 249 | 250 | > var deployTxn = { from: alice, data: freeBeerBytecode, gas: 200000 } 251 | undefined 252 | 253 | > eth.estimateGas(deployTxn) 254 | 165815 255 | ``` 256 | 257 | Next, we'll create an instance of the contract from its ABI, and deploy it using the transaction. We can then obtain the deployed contract's address from the receipt (**Note:** The address of the deployed contract is `0x48c1bd...` in the example, but will be different for you): 258 | 259 | ``` 260 | # As alice: 261 | 262 | > var freeBeerContract = eth.contract(freeBeerAbi) 263 | undefined 264 | 265 | > var freeBeerInstance = freeBeerContract.new(deployTxn) 266 | undefined 267 | 268 | > var receipt = eth.getTransactionReceipt(freeBeerInstance.transactionHash) 269 | undefined 270 | 271 | > receipt.contractAddress 272 | "0x48c1bdb954c945a57459286719e1a3c86305fd9e" 273 | 274 | > receipt.gasUsed 275 | 165814 276 | ``` 277 | 278 | We see that we used 165,814 gas in the deployment--- just 1 off of our initial estimate! After deployment, Alice's account balance decreased by 2,984,652 Gwei (1 Gwei = 1 Shannon = 1 Nano Ether), which is just the cost of 165,814 gas as the prevailing price of 18 Gwei. 279 | 280 | ``` 281 | > eth.getBalance(alice) 282 | 997015348000000000 283 | 284 | > web3.toWei(1, "ether") - eth.getBalance(alice) == 165814 * eth.gasPrice 285 | true 286 | ``` 287 | 288 | ### Using FreeBeer to Transfer Money 289 | 290 | Now that our contract is deployed, let's have Bob use it to send some money to Alice via the contract. To do so, Bob will take the compiled ABI and bind it to the deployed contract's address. Bob can then use this contract to call the `.gimmeMoney` method, sending 100 Finneys (1 Finney = 1 milliEther) to the contract owner (Alice): 291 | 292 | ``` 293 | # As bob: 294 | 295 | > loadScript('solidity/FreeBeer.sol.js') 296 | true 297 | 298 | > var freeBeerContract = eth.contract(freeBeerAbi) 299 | undefined 300 | 301 | > var freeBeerDeployed = freeBeerContract.at("0x48c1bdb954c945a57459286719e1a3c86305fd9e") 302 | undefined 303 | 304 | > freeBeerDeployed.gimmeMoney.sendTransaction({ from: bob, value: web3.toWei(0.1, 'ether')}) 305 | "0xe42b7d3d113f8670528ee5f14ec6cd65e94d15c12b4ca31187e1134c80e884ff" 306 | ``` 307 | 308 | Checking our account balances after the transaction shows that Alice's account has indeed increased by 100 Finneys, while Bob's has decreased by about 100.56 Finneys. Again, the discrepancy is due to the gas cost of executing the smart contract. In this case, the cost (at the prevailing gas price) was 30,979 gas: 309 | 310 | ``` 311 | > [ eth.getBalance(alice), eth.getBalance(bob)] 312 | [1097015348000000000, 899442378000000000] 313 | ``` 314 | 315 | ### Events on Smart Contracts 316 | 317 | The last concept we'll cover are events. Each time money is successfully sent, the contract emits an [event](https://github.com/vincentchu/eth-private-net/blob/master/solidity/FreeBeer.sol#L31). The following calls will allow us examine these events in greater detail. 318 | 319 | ``` 320 | > var outputEvent = function (e, result) { console.log(JSON.stringify(result)); } 321 | undefined 322 | 323 | > freeBeerDeployed.MoneySent({}, { fromBlock: 0, toBlock: 'latest' }).get(outputEvent) 324 | [ 325 | { 326 | "address": "0x48c1bdb954c945a57459286719e1a3c86305fd9e", 327 | "args": { 328 | "amount": "100000000000000000", 329 | "recipient": "0xdda6ef2ff259928c561b2d30f0cad2c2736ce8b6", 330 | "sender": "0x8691bf25ce4a56b15c1f99c944dc948269031801" 331 | }, 332 | "blockHash": "0xe05737f1a0cbebac566e21e9d22986d36a50f42e115c058e7fe45cf14429dd4f", 333 | "blockNumber": 111, 334 | "event": "MoneySent", 335 | "logIndex": 0, 336 | "removed": false, 337 | "transactionHash": "0xf838b6be4d68de91ac1af91b933bb5bdfcae1e49c287184795b7933e654f2c14", 338 | "transactionIndex": 0 339 | } 340 | ] 341 | ``` 342 | 343 | ## Fin 344 | 345 | I hope you've enjoyed this tutorial. I wrote it mostly to help my own understanding about smart contracts and the Ethereum blockchain. It's by no means complete, but hopefully others can get some use out of it. Questions, comments, or hate? Find me on Twitter as [@vincentchu](https://twitter.com/vincentchu). 346 | 347 | _Building something interesting? [Initialized Capital](https://twitter.com/@initializedcap) would love to chat with you._ 348 | 349 | _Thanks:_ Brett Gibson, [Sachin Agarwal](https://twitter.com/agarwal), [Alina Libova](https://twitter.com/alina_libova), and [Kim-Mai Cutler](https://twitter.com/kimmaicutler) for reading through drafts of this tutorial and providing insightful feedback and comments. 350 | --------------------------------------------------------------------------------