├── README.md ├── code ├── OpenZeppelin │ ├── contracts │ │ ├── SampleCrowdsale.sol │ │ └── SampleToken.sol │ └── migrations │ │ └── 2_deploy_contracts.js ├── Solidity │ ├── Faucet.sol │ ├── Faucet2.sol │ ├── Faucet3.sol │ ├── Faucet4.sol │ ├── Faucet5.sol │ ├── Faucet6.sol │ ├── Faucet7.sol │ ├── Faucet8.sol │ └── Token.sol ├── auction_dapp │ ├── .gitignore │ ├── DEV_README.md │ ├── README.md │ ├── auction_diagram.png │ ├── backend │ │ ├── .gitignore │ │ ├── contracts │ │ │ ├── AuctionRepository.sol │ │ │ ├── DeedRepository.sol │ │ │ ├── ERC721 │ │ │ │ ├── DeprecatedERC721.sol │ │ │ │ ├── ERC721.sol │ │ │ │ ├── ERC721Basic.sol │ │ │ │ ├── ERC721BasicToken.sol │ │ │ │ ├── ERC721Holder.sol │ │ │ │ ├── ERC721Receiver.sol │ │ │ │ └── ERC721Token.sol │ │ │ ├── Migrations.sol │ │ │ └── utils │ │ │ │ ├── AddressUtils.sol │ │ │ │ └── math │ │ │ │ ├── Math.sol │ │ │ │ └── SafeMath.sol │ │ ├── migrations │ │ │ ├── 1_initial_migration.js │ │ │ └── 2_deploy_contracts.js │ │ ├── scripts │ │ │ └── prepare.js │ │ ├── test │ │ │ ├── 1_auctionrepository.js │ │ │ ├── 2_deedrepository.js │ │ │ └── output.address │ │ ├── truffle-config.js │ │ └── truffle.js │ └── frontend │ │ ├── .babelrc │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── .postcssrc.js │ │ ├── README.md │ │ ├── config │ │ ├── dev.env.js │ │ ├── index.js │ │ └── prod.env.js │ │ ├── index.html │ │ ├── src │ │ ├── App.vue │ │ ├── components │ │ │ ├── Auction.vue │ │ │ └── Home.vue │ │ ├── config.js │ │ ├── contracts │ │ │ ├── AuctionRepository.json │ │ │ └── DeedRepository.json │ │ ├── main.js │ │ ├── models │ │ │ ├── AuctionRepository.js │ │ │ ├── ChatRoom.js │ │ │ └── DeedRepository.js │ │ └── router │ │ │ └── index.js │ │ └── static │ │ └── .gitkeep ├── aws │ ├── genesis.json │ └── truffle.js ├── jsonrpc │ ├── http │ │ └── js │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ └── index.js │ ├── ipc │ │ └── client.go │ └── websockets │ │ ├── .gitignore │ │ ├── README.md │ │ └── app.js ├── jupyter_notebook │ ├── .ipynb_checkpoints │ │ └── keys_and_addresses_python_notebook-checkpoint.ipynb │ └── keys_and_addresses_python_notebook.ipynb ├── misc │ ├── example_keys.txt │ └── readme_wordcount.py ├── truffle │ ├── CallExamples │ │ ├── contracts │ │ │ ├── CallExamples.sol │ │ │ └── Migrations.sol │ │ ├── migrations │ │ │ ├── 1_initial_migration.js │ │ │ └── 2_deploy_contracts.js │ │ └── truffle-config.js │ ├── Faucet │ │ ├── contracts │ │ │ ├── Faucet.sol │ │ │ └── Migrations.sol │ │ ├── migrations │ │ │ ├── 1_initial_migration.js │ │ │ └── 2_deploy_contracts.js │ │ └── truffle-config.js │ ├── FaucetEvents │ │ ├── contracts │ │ │ ├── Faucet.sol │ │ │ └── Migrations.sol │ │ ├── gas_estimates.js │ │ ├── migrations │ │ │ ├── 1_initial_migration.js │ │ │ └── 2_deploy_contracts.js │ │ └── truffle-config.js │ ├── METoken │ │ ├── contracts │ │ │ ├── METoken.sol │ │ │ └── Migrations.sol │ │ ├── migrations │ │ │ ├── 1_initial_migration.js │ │ │ └── 2_deploy_contracts.js │ │ ├── test │ │ │ └── METoken.test.js │ │ └── truffle-config.js │ ├── METoken_Faucet │ │ ├── contracts │ │ │ ├── Faucet.sol │ │ │ ├── METoken.sol │ │ │ └── Migrations.sol │ │ ├── migrations │ │ │ ├── 1_initial_migration.js │ │ │ ├── 2_deploy_contracts.js │ │ │ └── 3_deploy_faucet.js │ │ └── truffle-config.js │ ├── METoken_METFaucet │ │ ├── contracts │ │ │ ├── METFaucet.sol │ │ │ ├── METoken.sol │ │ │ └── Migrations.sol │ │ ├── migrations │ │ │ ├── 1_initial_migration.js │ │ │ └── 2_deploy_contracts.js │ │ ├── test │ │ │ └── test_approve_transferFrom.js │ │ └── truffle-config.js │ └── console │ │ ├── contracts │ │ └── Migrations.sol │ │ ├── migrations │ │ └── 1_initial_migration.js │ │ ├── nonce.js │ │ └── truffle-config.js └── web3js │ ├── raw_tx │ └── raw_tx_demo.js │ └── web3js_demo │ ├── web3-contract-basic-interaction-async-await.js │ └── web3-contract-basic-interaction.js ├── cover.jpg ├── images ├── Faucet_disassembled.png ├── Faucet_jumpi_instruction.png ├── TransferToFriend.gif ├── approve_transferFrom_workflow.png ├── architecture_diagram_webapp.png ├── aws-topology-simple-bootnode.png ├── bip39-part1.png ├── bip39-part2.png ├── bip39_web.png ├── block_explorer_account_history.png ├── branches.png ├── chat-on-gitter.svg ├── contract_published.png ├── cover.png ├── cover_thumb.png ├── ec_over_small_prime_field.png ├── eip_workflow.png ├── ens-checkname.png ├── ens-flow.png ├── etherscan_contract_address.png ├── etherscan_withdrawal_internal.png ├── etherscan_withdrawal_tx.png ├── ganache_metoken.png ├── ganache_metoken_transfer.png ├── hd_wallet.png ├── http_ws_ipc_jsonrpc.png ├── metamask_account.png ├── metamask_account_context_menu.png ├── metamask_download.png ├── metamask_mnemonic.png ├── metamask_password.png ├── metamask_ropsten_faucet.png ├── metamask_send_to_contract.png ├── metamask_withdraw.png ├── newproj.png ├── parity_deployment_confirmation.png ├── parity_txdemo_novalue_data.png ├── parity_txdemo_novalue_nodata.png ├── parity_txdemo_value_data.png ├── parity_txdemo_value_nodata.png ├── proxy-lib.png ├── published_contract_transactions.png ├── quickbuilds.png ├── remix_close_tab.png ├── remix_compile.png ├── remix_contract_address.png ├── remix_contract_interact.png ├── remix_create_contract.png ├── remix_faucet_load.png ├── remix_metamask_create.png ├── remix_run.png ├── remix_toolbar.png ├── remix_withdraw.png ├── rlpx_rpc_xs.png ├── ropsten_block_explorer.png ├── send_to_faucet.png ├── simple_elliptic_curve.png ├── tarsier.png ├── thanks.jpeg ├── theme.png └── web3suite.png ├── 前言.asciidoc ├── 术语.asciidoc ├── 第一章.asciidoc ├── 第七章.asciidoc ├── 第三章.asciidoc ├── 第九章.asciidoc ├── 第二章.asciidoc ├── 第五章.asciidoc ├── 第八章.asciidoc ├── 第六章.asciidoc ├── 第十一章.asciidoc ├── 第十七章.asciidoc ├── 第十三章.asciidoc ├── 第十九章.asciidoc ├── 第十二章.asciidoc ├── 第十五章.asciidoc ├── 第十八章.asciidoc ├── 第十六章.asciidoc ├── 第十四章.asciidoc ├── 第十章.asciidoc └── 第四章.asciidoc /README.md: -------------------------------------------------------------------------------- 1 | ### [《精通以太坊——实现数字合约》](https://github.com/ethereumbook/ethereumbook) 中文版 2 | 3 | ### 关于译者 4 | 互联网从业者,对经济学感兴趣,很早加入数字货币和区块链研究。 5 | 6 | 闲暇时间翻译,欢迎指正,感谢加星~ 7 | 8 | ### 封面 9 | ![Mastering Ethereum](images/cover.png) 10 | 11 | ### 目录 12 | 13 | [前言](前言.asciidoc) 14 | 15 | [术语](术语.asciidoc) 16 | 17 | [第一章 什么是以太坊](第一章.asciidoc) 18 | 19 | [第二章 以太坊基础](第二章.asciidoc) 20 | 21 | [第三章 以太坊客户端](第三章.asciidoc) 22 | 23 | [第四章 以太坊测试网](第四章.asciidoc) 24 | 25 | [第五章 密钥和地址](第五章.asciidoc) 26 | 27 | [第六章 钱包](第六章.asciidoc) 28 | 29 | [第七章 交易](第七章.asciidoc) 30 | 31 | [第八章 智能合约](第八章.asciidoc) 32 | 33 | [第九章 开发工具,框架和库](第九章.asciidoc) 34 | 35 | [第十章 代币(Tokens)](第十章.asciidoc) 36 | 37 | [第十一章 去中心化应用(DApps)](第十一章.asciidoc) 38 | 39 | [第十二章 预言机(Oracles)](第十二章.asciidoc) 40 | 41 | [第十三章 燃气(Gas)](第十三章.asciidoc) 42 | 43 | [第十四章 以太坊虚拟机](第十四章.asciidoc) 44 | 45 | [第十五章 共识](第十五章.asciidoc) 46 | 47 | [第十六章 Vyper:面向合约的编程语言](第十六章.asciidoc) 48 | 49 | [第十七章 DevP2P协议](第十七章.asciidoc) 50 | 51 | [第十八章 以太坊标准](第十八章.asciidoc) 52 | 53 | [第十九章 以太坊分叉历史](第十九章.asciidoc) 54 | 55 | 56 | ### 打赏 57 | ![](images/thanks.jpeg) 58 | -------------------------------------------------------------------------------- /code/OpenZeppelin/contracts/SampleCrowdsale.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.23; 2 | 3 | import './SampleToken.sol'; 4 | import 'openzeppelin-solidity/contracts/crowdsale/emission/MintedCrowdsale.sol'; 5 | import 'openzeppelin-solidity/contracts/crowdsale/distribution/PostDeliveryCrowdsale.sol'; 6 | 7 | contract SampleCrowdsale is PostDeliveryCrowdsale, MintedCrowdsale { 8 | 9 | constructor( 10 | uint256 _openingTime, 11 | uint256 _closingTime 12 | uint256 _rate, 13 | address _wallet, 14 | MintableToken _token 15 | ) 16 | public 17 | Crowdsale(_rate, _wallet, _token) 18 | PostDeliveryCrowdsale(_openingTime, _closingTime) 19 | { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /code/OpenZeppelin/contracts/SampleToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.23; 2 | 3 | import 'openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol'; 4 | 5 | contract SampleToken is MintableToken { 6 | string public name = "SAMPLE TOKEN"; 7 | string public symbol = "SAM"; 8 | uint8 public decimals = 18; 9 | } 10 | -------------------------------------------------------------------------------- /code/OpenZeppelin/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const SampleCrowdsale = artifacts.require('./SampleCrowdsale.sol'); 2 | const SampleToken = artifacts.require('./SampleToken.sol'); 3 | 4 | module.exports = function(deployer, network, accounts) { 5 | const openingTime = web3.eth.getBlock('latest').timestamp + 2; // two secs in the future 6 | const closingTime = openingTime + 86400 * 20; // 20 days 7 | const rate = new web3.BigNumber(1000); 8 | const wallet = accounts[1]; 9 | 10 | return deployer 11 | .then(() => { 12 | return deployer.deploy(SampleToken); 13 | }) 14 | .then(() => { 15 | return deployer.deploy( 16 | SampleCrowdsale, 17 | openingTime, 18 | closingTime, 19 | rate, 20 | wallet, 21 | SampleToken.address 22 | ); 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /code/Solidity/Faucet.sol: -------------------------------------------------------------------------------- 1 | // Our first contract is a faucet! 2 | contract Faucet { 3 | 4 | // Give out ether to anyone who asks 5 | function withdraw(uint withdraw_amount) public { 6 | 7 | // Limit withdrawal amount 8 | require(withdraw_amount <= 100000000000000000); 9 | 10 | // Send the amount to the address that requested it 11 | msg.sender.transfer(withdraw_amount); 12 | } 13 | 14 | // Accept any incoming amount 15 | function () public payable {} 16 | 17 | } 18 | -------------------------------------------------------------------------------- /code/Solidity/Faucet2.sol: -------------------------------------------------------------------------------- 1 | // Version of Solidity compiler this program was written for 2 | pragma solidity ^0.4.19; 3 | 4 | // Our first contract is a faucet! 5 | contract Faucet { 6 | 7 | // Give out ether to anyone who asks 8 | function withdraw(uint withdraw_amount) public { 9 | 10 | // Limit withdrawal amount 11 | require(withdraw_amount <= 100000000000000000); 12 | 13 | // Send the amount to the address that requested it 14 | msg.sender.transfer(withdraw_amount); 15 | } 16 | 17 | // Accept any incoming amount 18 | function () public payable {} 19 | 20 | } 21 | -------------------------------------------------------------------------------- /code/Solidity/Faucet3.sol: -------------------------------------------------------------------------------- 1 | // Version of Solidity compiler this program was written for 2 | pragma solidity ^0.4.22; 3 | 4 | // Our first contract is a faucet! 5 | contract Faucet { 6 | 7 | address owner; 8 | 9 | // Contract constructor: set owner 10 | constructor() { 11 | owner = msg.sender; 12 | } 13 | 14 | // Contract destructor 15 | function destroy() public { 16 | require(msg.sender == owner); 17 | selfdestruct(owner); 18 | } 19 | 20 | // Give out ether to anyone who asks 21 | function withdraw(uint withdraw_amount) public { 22 | 23 | // Limit withdrawal amount 24 | require(withdraw_amount <= 0.1 ether); 25 | 26 | // Send the amount to the address that requested it 27 | msg.sender.transfer(withdraw_amount); 28 | } 29 | 30 | // Accept any incoming amount 31 | function () public payable {} 32 | 33 | } 34 | -------------------------------------------------------------------------------- /code/Solidity/Faucet4.sol: -------------------------------------------------------------------------------- 1 | // Version of Solidity compiler this program was written for 2 | pragma solidity ^0.4.22; 3 | 4 | // Our first contract is a faucet! 5 | contract Faucet { 6 | 7 | address owner; 8 | 9 | // Contract constructor: set owner 10 | constructor() { 11 | owner = msg.sender; 12 | } 13 | 14 | // Access control modifier 15 | modifier onlyOwner { 16 | require(msg.sender == owner); 17 | _; 18 | } 19 | 20 | // Contract destructor 21 | function destroy() public onlyOwner { 22 | selfdestruct(owner); 23 | } 24 | 25 | // Give out ether to anyone who asks 26 | function withdraw(uint withdraw_amount) public { 27 | 28 | // Limit withdrawal amount 29 | require(withdraw_amount <= 0.1 ether); 30 | 31 | // Send the amount to the address that requested it 32 | msg.sender.transfer(withdraw_amount); 33 | } 34 | 35 | // Accept any incoming amount 36 | function () public payable {} 37 | 38 | } 39 | -------------------------------------------------------------------------------- /code/Solidity/Faucet5.sol: -------------------------------------------------------------------------------- 1 | // Version of Solidity compiler this program was written for 2 | pragma solidity ^0.4.22; 3 | 4 | contract owned { 5 | address owner; 6 | // Contract constructor: set owner 7 | constructor() { 8 | owner = msg.sender; 9 | } 10 | // Access control modifier 11 | modifier onlyOwner { 12 | require(msg.sender == owner); 13 | _; 14 | } 15 | } 16 | 17 | contract mortal is owned { 18 | // Contract destructor 19 | function destroy() public onlyOwner { 20 | selfdestruct(owner); 21 | } 22 | } 23 | 24 | contract Faucet is mortal { 25 | // Give out ether to anyone who asks 26 | function withdraw(uint withdraw_amount) public { 27 | // Limit withdrawal amount 28 | require(withdraw_amount <= 0.1 ether); 29 | // Send the amount to the address that requested it 30 | msg.sender.transfer(withdraw_amount); 31 | } 32 | // Accept any incoming amount 33 | function () public payable {} 34 | } 35 | -------------------------------------------------------------------------------- /code/Solidity/Faucet6.sol: -------------------------------------------------------------------------------- 1 | // Version of Solidity compiler this program was written for 2 | pragma solidity ^0.4.22; 3 | 4 | contract owned { 5 | address owner; 6 | // Contract constructor: set owner 7 | constructor() { 8 | owner = msg.sender; 9 | } 10 | // Access control modifier 11 | modifier onlyOwner { 12 | require(msg.sender == owner, "Only the contract owner can call this function"); 13 | _; 14 | } 15 | } 16 | 17 | contract mortal is owned { 18 | // Contract destructor 19 | function destroy() public onlyOwner { 20 | selfdestruct(owner); 21 | } 22 | } 23 | 24 | contract Faucet is mortal { 25 | // Give out ether to anyone who asks 26 | function withdraw(uint withdraw_amount) public { 27 | // Limit withdrawal amount 28 | require(withdraw_amount <= 0.1 ether); 29 | // Send the amount to the address that requested it 30 | msg.sender.transfer(withdraw_amount); 31 | } 32 | // Accept any incoming amount 33 | function () public payable {} 34 | } 35 | -------------------------------------------------------------------------------- /code/Solidity/Faucet7.sol: -------------------------------------------------------------------------------- 1 | // Version of Solidity compiler this program was written for 2 | pragma solidity ^0.4.22; 3 | 4 | contract owned { 5 | address owner; 6 | // Contract constructor: set owner 7 | constructor() { 8 | owner = msg.sender; 9 | } 10 | // Access control modifier 11 | modifier onlyOwner { 12 | require(msg.sender == owner, "Only the contract owner can call this function"); 13 | _; 14 | } 15 | } 16 | 17 | contract mortal is owned { 18 | // Contract destructor 19 | function destroy() public onlyOwner { 20 | selfdestruct(owner); 21 | } 22 | } 23 | 24 | contract Faucet is mortal { 25 | // Give out ether to anyone who asks 26 | function withdraw(uint withdraw_amount) public { 27 | // Limit withdrawal amount 28 | require(withdraw_amount <= 0.1 ether); 29 | require(this.balance >= withdraw_amount, 30 | "Insufficient balance in faucet for withdrawal request") 31 | // Send the amount to the address that requested it 32 | msg.sender.transfer(withdraw_amount); 33 | } 34 | // Accept any incoming amount 35 | function () public payable {} 36 | } 37 | -------------------------------------------------------------------------------- /code/Solidity/Faucet8.sol: -------------------------------------------------------------------------------- 1 | // Version of Solidity compiler this program was written for 2 | pragma solidity ^0.4.22; 3 | 4 | contract owned { 5 | address owner; 6 | // Contract constructor: set owner 7 | constructor() { 8 | owner = msg.sender; 9 | } 10 | // Access control modifier 11 | modifier onlyOwner { 12 | require(msg.sender == owner, "Only the contract owner can call this function"); 13 | _; 14 | } 15 | } 16 | 17 | contract mortal is owned { 18 | // Contract destructor 19 | function destroy() public onlyOwner { 20 | selfdestruct(owner); 21 | } 22 | } 23 | 24 | contract Faucet is mortal { 25 | event Withdrawal(address indexed to, uint amount); 26 | event Deposit(address indexed from, uint amount); 27 | 28 | // Give out ether to anyone who asks 29 | function withdraw(uint withdraw_amount) public { 30 | // Limit withdrawal amount 31 | require(withdraw_amount <= 0.1 ether); 32 | require(this.balance >= withdraw_amount, 33 | "Insufficient balance in faucet for withdrawal request"); 34 | // Send the amount to the address that requested it 35 | msg.sender.transfer(withdraw_amount); 36 | emit Withdrawal(msg.sender, withdraw_amount); 37 | } 38 | // Accept any incoming amount 39 | function () public payable { 40 | emit Deposit(msg.sender, msg.value); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /code/Solidity/Token.sol: -------------------------------------------------------------------------------- 1 | import "Faucet.sol" 2 | 3 | contract Token is mortal { 4 | 5 | Faucet _faucet; 6 | 7 | constructor(address _f) { 8 | _faucet = Faucet(_f); 9 | _faucet.withdraw(0.1 ether) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /code/auction_dapp/.gitignore: -------------------------------------------------------------------------------- 1 | _local_dev/ 2 | -------------------------------------------------------------------------------- /code/auction_dapp/DEV_README.md: -------------------------------------------------------------------------------- 1 | # Configuring truffle 2 | Guide to properly configure truffle to build and deploy contracts to the network 3 | 4 | 5 | ## Add local testnet 6 | 7 | Edit `truffle.js` : 8 | 9 | ``` 10 | networks: { 11 | development: { 12 | host: "", 13 | port: 8545, 14 | network_id: "*", 15 | gas: 2900000 16 | } 17 | } 18 | ``` 19 | 20 | ## Commands 21 | 22 | ``` 23 | $ truffle init #initialize truffle project 24 | $ truffle console 25 | $ truffle deploy 26 | $ truffle migrate --reset --compile-all #redeploy the smat contracts 27 | 28 | ``` 29 | 30 | ## Errors 31 | Here is a list of possible errors: 32 | 33 | ## ParserError: Expected token Semicolon got 'LParen' 34 | 35 | Emitting events gives compilation error if solc-js not 0.4.21 36 | Update truffle using: 37 | 38 | ``` 39 | $ cd /usr/lib/node_modules/truffle 40 | $ npm install solc@0.4.21 --save 41 | ``` 42 | 43 | ### Account is locked/Auth needed 44 | 45 | ``` 46 | $ truffle console 47 | truffle(development)> var acc = web3.eth.accounts[0] 48 | truffle(development)> web3.personal.unlockAccount(acc, 'pass', 36000) 49 | ``` 50 | 51 | Then deploy. Repeat the step when needed 52 | 53 | ### Intrinsic gas too low 54 | 55 | ``` 56 | $ truffle console 57 | truffle(development)> web3.eth.getBlock("pending").gasLimit 58 | xxxxxxx 59 | ``` 60 | 61 | edit truffle.js 62 | 63 | ``` 64 | development: { 65 | ... 66 | gas: xxxxxxxxx, 67 | network_id: "*" 68 | } 69 | ``` 70 | -------------------------------------------------------------------------------- /code/auction_dapp/README.md: -------------------------------------------------------------------------------- 1 | # Decentralized Auction Application on Ethereum 2 | 3 | This project aims to implement basic functionalities of an auction platform using Ethereum. 4 | 5 | ## Requirements 6 | 7 | ![Auction Diagram](auction_diagram.png) 8 | 9 | The application should be decentralized and utilize Ethereum's stack: 10 | 11 | 1. Smart contracts for business logic(auctions, bids, refund and transfer of ownership) 12 | 2. Swarm for data storage(image and metadata) 13 | 3. Whisper for a peer-to-peer messaging(chatrooms) 14 | 15 | ### Deed Repository 16 | Manage non-fungible tokens by implementing an asset/token/deed repository which holds unique asset/token/deed. 17 | 18 | #### ERC: Non-fungible Token Standard #721 (NFT) 19 | See following link: 20 | https://github.com/ethereum/eips/issues/721 21 | 22 | ### Auction Repository 23 | 24 | Auction repository MUST act as an auction house which does the following: 25 | 26 | - Holds asset/token/deed that is to be auctioned(ERC721 Ownership by smart contract) 27 | - Allows users bid on auctions 28 | - Keeps track of auctions/bids/ownership 29 | - Transfers ownership of asset/token/deed to winder 30 | - Transfers Funds to auction creator if auction is ended and there is at least one winner 31 | - Cancels auction and deal with refunds 32 | - UI to interact with the above functionality 33 | 34 | ### Front-end: Vuejs2.x + Vuetify 35 | 36 | The front-end is developed using a reactive UI framework with integration of Vuetify, a Google's Material Design implementation. 37 | 38 | ## Implementation/Data flow 39 | 40 | #### 1. Register an ERC721 Non-Fungible Token with the AuctionDaap Deed Repository 41 | 42 | The idea of a Deed Repository is used across this project to hold any NFT with metadata attached to. A token/deed is registered by giving a unique ID and attaching metadata(TokenURI). The metadata is what makes each token important or valuable. 43 | 44 | #### 2. Transfer Ownership of the NFT to AuctionRepository(Auction house) 45 | 46 | The Auction house needs to verify that a NFT is owned by the auction creator, therefore before the auction is created, the owner should transfer the ownership of the NFT to the AuctionRepository smart contract address. 47 | 48 | #### 3. Create Auction for NFT 49 | 50 | Creating the auction is a simple process of entering auction details such as name, strarting price, expirty date etc. The important part is to have the reference between the deed and the auction. 51 | 52 | #### 4. Bid on Auction 53 | 54 | Anyone can bid on an auction except the owner of the auction. Biding means that previous bidders are refunded and new bid is placed. Bid requirements are as follow: 55 | 1. Auction not expired 56 | 2. Bidder is not auction owner 57 | 3. Bid amount is greator than current bid or starting price(if no bid) 58 | 59 | #### 5. Refunds 60 | 61 | If an auction is canceled, the Auction Repository MUST return the ownership of the asset/token/deed back to the auction creator and refund bidders if any. 62 | 63 | #### 5. Bidder Win Auction 64 | 65 | If there is an auction winner, the asset/token/deed is transfered to the bidder and the bid amount is sent to the auction creator. 66 | 67 | -------------------------------------------------------------------------------- /code/auction_dapp/auction_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/code/auction_dapp/auction_diagram.png -------------------------------------------------------------------------------- /code/auction_dapp/backend/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/contracts/DeedRepository.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | import "./ERC721/ERC721Token.sol"; 3 | 4 | /** 5 | * @title Repository of ERC721 Deeds 6 | * This contract contains the list of deeds registered by users. 7 | * This is a demo to show how tokens(deeds) can be mint and add to the repository 8 | */ 9 | contract DeedRepository is ERC721Token { 10 | 11 | /** 12 | * @dev Created a DeedRepository with a name and symbol 13 | * @param _name string represents the name of the repository 14 | * @param _symbol string represents the symbol of the repositor 15 | */ 16 | function DeedRepository(string _name, string _symbol) public ERC721Token(_name, _symbol) {} 17 | 18 | /** 19 | * @dev Public function to register a new deed 20 | * @dev Call the ERC721Token Minter 21 | * @param _tokenId uint256 represents a specific deed 22 | * @param _uri string containing metadata/uri 23 | */ 24 | function registerDeed(uint256 _tokenId, string _uri) public { 25 | _mint(msg.sender, _tokenId); 26 | addDeedMetadata(_tokenId, _uri); 27 | emit DeedRegistered(msg.sender, _tokenId); 28 | } 29 | 30 | /** 31 | * @dev Public function to add metadata to a deed 32 | * @param _tokenId represents a specific deed 33 | * @param _uri text which describes the characteristics of a given deed 34 | * @return whether the deed metadata was added to the repository 35 | */ 36 | function addDeedMetadata(uint256 _tokenId, string _uri) public returns(bool){ 37 | _setTokenURI(_tokenId, _uri); 38 | return true; 39 | } 40 | 41 | /** 42 | * @dev Event is triggered if deed/token is registered 43 | * @param _by address of the registrar 44 | * @param _tokenId uint256 represents a specific deed 45 | */ 46 | event DeedRegistered(address _by, uint256 _tokenId); 47 | } -------------------------------------------------------------------------------- /code/auction_dapp/backend/contracts/ERC721/DeprecatedERC721.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import "./ERC721.sol"; 4 | 5 | 6 | /** 7 | * @title ERC-721 methods shipped in OpenZeppelin v1.7.0, removed in the latest version of the standard 8 | * @dev Only use this interface for compatibility with previously deployed contracts 9 | * @dev Use ERC721 for interacting with new contracts which are standard-compliant 10 | */ 11 | contract DeprecatedERC721 is ERC721 { 12 | function takeOwnership(uint256 _tokenId) public; 13 | function transfer(address _to, uint256 _tokenId) public; 14 | function tokensOf(address _owner) public view returns (uint256[]); 15 | } 16 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/contracts/ERC721/ERC721.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import "./ERC721Basic.sol"; 4 | 5 | 6 | /** 7 | * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension 8 | * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md 9 | */ 10 | contract ERC721Enumerable is ERC721Basic { 11 | function totalSupply() public view returns (uint256); 12 | function tokenOfOwnerByIndex(address _owner, uint256 _index) public view returns (uint256 _tokenId); 13 | function tokenByIndex(uint256 _index) public view returns (uint256); 14 | } 15 | 16 | 17 | /** 18 | * @title ERC-721 Non-Fungible Token Standard, optional metadata extension 19 | * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md 20 | */ 21 | contract ERC721Metadata is ERC721Basic { 22 | function name() public view returns (string _name); 23 | function symbol() public view returns (string _symbol); 24 | function tokenURI(uint256 _tokenId) public view returns (string); 25 | } 26 | 27 | 28 | /** 29 | * @title ERC-721 Non-Fungible Token Standard, full implementation interface 30 | * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md 31 | */ 32 | contract ERC721 is ERC721Basic, ERC721Enumerable, ERC721Metadata { 33 | } 34 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/contracts/ERC721/ERC721Basic.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | 4 | /** 5 | * @title ERC721 Non-Fungible Token Standard basic interface 6 | * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md 7 | */ 8 | contract ERC721Basic { 9 | event Transfer(address indexed _from, address indexed _to, uint256 _tokenId); 10 | event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId); 11 | event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); 12 | 13 | function balanceOf(address _owner) public view returns (uint256 _balance); 14 | function ownerOf(uint256 _tokenId) public view returns (address _owner); 15 | function exists(uint256 _tokenId) public view returns (bool _exists); 16 | 17 | function approve(address _to, uint256 _tokenId) public; 18 | function getApproved(uint256 _tokenId) public view returns (address _operator); 19 | 20 | function setApprovalForAll(address _operator, bool _approved) public; 21 | function isApprovedForAll(address _owner, address _operator) public view returns (bool); 22 | 23 | function transferFrom(address _from, address _to, uint256 _tokenId) public; 24 | function safeTransferFrom(address _from, address _to, uint256 _tokenId) public; 25 | function safeTransferFrom( 26 | address _from, 27 | address _to, 28 | uint256 _tokenId, 29 | bytes _data 30 | ) 31 | public; 32 | } 33 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/contracts/ERC721/ERC721Holder.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import "./ERC721Receiver.sol"; 4 | 5 | 6 | contract ERC721Holder is ERC721Receiver { 7 | function onERC721Received(address, uint256, bytes) public returns(bytes4) { 8 | return ERC721_RECEIVED; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/contracts/ERC721/ERC721Receiver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | 4 | /** 5 | * @title ERC721 token receiver interface 6 | * @dev Interface for any contract that wants to support safeTransfers 7 | * from ERC721 asset contracts. 8 | */ 9 | contract ERC721Receiver { 10 | /** 11 | * @dev Magic value to be returned upon successful reception of an NFT 12 | * Equals to `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`, 13 | * which can be also obtained as `ERC721Receiver(0).onERC721Received.selector` 14 | */ 15 | bytes4 constant ERC721_RECEIVED = 0xf0b9e5ba; 16 | 17 | /** 18 | * @notice Handle the receipt of an NFT 19 | * @dev The ERC721 smart contract calls this function on the recipient 20 | * after a `safetransfer`. This function MAY throw to revert and reject the 21 | * transfer. This function MUST use 50,000 gas or less. Return of other 22 | * than the magic value MUST result in the transaction being reverted. 23 | * Note: the contract address is always the message sender. 24 | * @param _from The sending address 25 | * @param _tokenId The NFT identifier which is being transfered 26 | * @param _data Additional data with no specified format 27 | * @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))` 28 | */ 29 | function onERC721Received(address _from, uint256 _tokenId, bytes _data) public returns(bytes4); 30 | } 31 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/contracts/ERC721/ERC721Token.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import "./ERC721.sol"; 4 | import "./DeprecatedERC721.sol"; 5 | import "./ERC721BasicToken.sol"; 6 | 7 | 8 | /** 9 | * @title Full ERC721 Token 10 | * This implementation includes all the required and some optional functionality of the ERC721 standard 11 | * Moreover, it includes approve all functionality using operator terminology 12 | * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md 13 | */ 14 | contract ERC721Token is ERC721, ERC721BasicToken { 15 | // Token name 16 | string internal name_; 17 | 18 | // Token symbol 19 | string internal symbol_; 20 | 21 | // Mapping from owner to list of owned token IDs 22 | mapping (address => uint256[]) internal ownedTokens; 23 | 24 | // Mapping from token ID to index of the owner tokens list 25 | mapping(uint256 => uint256) internal ownedTokensIndex; 26 | 27 | // Array with all token ids, used for enumeration 28 | uint256[] internal allTokens; 29 | 30 | // Mapping from token id to position in the allTokens array 31 | mapping(uint256 => uint256) internal allTokensIndex; 32 | 33 | // Optional mapping for token URIs 34 | mapping(uint256 => string) internal tokenURIs; 35 | 36 | /** 37 | * @dev Constructor function 38 | */ 39 | function ERC721Token(string _name, string _symbol) public { 40 | name_ = _name; 41 | symbol_ = _symbol; 42 | } 43 | 44 | /** 45 | * @dev Gets the token name 46 | * @return string representing the token name 47 | */ 48 | function name() public view returns (string) { 49 | return name_; 50 | } 51 | 52 | /** 53 | * @dev Gets the token symbol 54 | * @return string representing the token symbol 55 | */ 56 | function symbol() public view returns (string) { 57 | return symbol_; 58 | } 59 | 60 | /** 61 | * @dev Returns an URI for a given token ID 62 | * @dev Throws if the token ID does not exist. May return an empty string. 63 | * @param _tokenId uint256 ID of the token to query 64 | */ 65 | function tokenURI(uint256 _tokenId) public view returns (string) { 66 | require(exists(_tokenId)); 67 | return tokenURIs[_tokenId]; 68 | } 69 | 70 | /** 71 | * @dev Gets the token ID at a given index of the tokens list of the requested owner 72 | * @param _owner address owning the tokens list to be accessed 73 | * @param _index uint256 representing the index to be accessed of the requested tokens list 74 | * @return uint256 token ID at the given index of the tokens list owned by the requested address 75 | */ 76 | function tokenOfOwnerByIndex(address _owner, uint256 _index) public view returns (uint256) { 77 | require(_index < balanceOf(_owner)); 78 | return ownedTokens[_owner][_index]; 79 | } 80 | 81 | /** 82 | * @dev Gets the total amount of tokens stored by the contract 83 | * @return uint256 representing the total amount of tokens 84 | */ 85 | function totalSupply() public view returns (uint256) { 86 | return allTokens.length; 87 | } 88 | 89 | /** 90 | * @dev Gets the token ID at a given index of all the tokens in this contract 91 | * @dev Reverts if the index is greater or equal to the total number of tokens 92 | * @param _index uint256 representing the index to be accessed of the tokens list 93 | * @return uint256 token ID at the given index of the tokens list 94 | */ 95 | function tokenByIndex(uint256 _index) public view returns (uint256) { 96 | require(_index < totalSupply()); 97 | return allTokens[_index]; 98 | } 99 | 100 | /** 101 | * @dev Internal function to set the token URI for a given token 102 | * @dev Reverts if the token ID does not exist 103 | * @param _tokenId uint256 ID of the token to set its URI 104 | * @param _uri string URI to assign 105 | */ 106 | function _setTokenURI(uint256 _tokenId, string _uri) internal { 107 | require(exists(_tokenId)); 108 | tokenURIs[_tokenId] = _uri; 109 | } 110 | 111 | /** 112 | * @dev Internal function to add a token ID to the list of a given address 113 | * @param _to address representing the new owner of the given token ID 114 | * @param _tokenId uint256 ID of the token to be added to the tokens list of the given address 115 | */ 116 | function addTokenTo(address _to, uint256 _tokenId) internal { 117 | super.addTokenTo(_to, _tokenId); 118 | uint256 length = ownedTokens[_to].length; 119 | ownedTokens[_to].push(_tokenId); 120 | ownedTokensIndex[_tokenId] = length; 121 | } 122 | 123 | /** 124 | * @dev Internal function to remove a token ID from the list of a given address 125 | * @param _from address representing the previous owner of the given token ID 126 | * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address 127 | */ 128 | function removeTokenFrom(address _from, uint256 _tokenId) internal { 129 | super.removeTokenFrom(_from, _tokenId); 130 | 131 | uint256 tokenIndex = ownedTokensIndex[_tokenId]; 132 | uint256 lastTokenIndex = ownedTokens[_from].length.sub(1); 133 | uint256 lastToken = ownedTokens[_from][lastTokenIndex]; 134 | 135 | ownedTokens[_from][tokenIndex] = lastToken; 136 | ownedTokens[_from][lastTokenIndex] = 0; 137 | // Note that this will handle single-element arrays. In that case, both tokenIndex and lastTokenIndex are going to 138 | // be zero. Then we can make sure that we will remove _tokenId from the ownedTokens list since we are first swapping 139 | // the lastToken to the first position, and then dropping the element placed in the last position of the list 140 | 141 | ownedTokens[_from].length--; 142 | ownedTokensIndex[_tokenId] = 0; 143 | ownedTokensIndex[lastToken] = tokenIndex; 144 | } 145 | 146 | /** 147 | * @dev Internal function to mint a new token 148 | * @dev Reverts if the given token ID already exists 149 | * @param _to address the beneficiary that will own the minted token 150 | * @param _tokenId uint256 ID of the token to be minted by the msg.sender 151 | */ 152 | function _mint(address _to, uint256 _tokenId) internal { 153 | super._mint(_to, _tokenId); 154 | 155 | allTokensIndex[_tokenId] = allTokens.length; 156 | allTokens.push(_tokenId); 157 | } 158 | 159 | /** 160 | * @dev Internal function to burn a specific token 161 | * @dev Reverts if the token does not exist 162 | * @param _owner owner of the token to burn 163 | * @param _tokenId uint256 ID of the token being burned by the msg.sender 164 | */ 165 | function _burn(address _owner, uint256 _tokenId) internal { 166 | super._burn(_owner, _tokenId); 167 | 168 | // Clear metadata (if any) 169 | if (bytes(tokenURIs[_tokenId]).length != 0) { 170 | delete tokenURIs[_tokenId]; 171 | } 172 | 173 | // Reorg all tokens array 174 | uint256 tokenIndex = allTokensIndex[_tokenId]; 175 | uint256 lastTokenIndex = allTokens.length.sub(1); 176 | uint256 lastToken = allTokens[lastTokenIndex]; 177 | 178 | allTokens[tokenIndex] = lastToken; 179 | allTokens[lastTokenIndex] = 0; 180 | 181 | allTokens.length--; 182 | allTokensIndex[_tokenId] = 0; 183 | allTokensIndex[lastToken] = tokenIndex; 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/contracts/utils/AddressUtils.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | 4 | /** 5 | * Utility library of inline functions on addresses 6 | */ 7 | library AddressUtils { 8 | 9 | /** 10 | * Returns whether the target address is a contract 11 | * @dev This function will return false if invoked during the constructor of a contract, 12 | * as the code is not actually created until after the constructor finishes. 13 | * @param addr address to check 14 | * @return whether the target address is a contract 15 | */ 16 | function isContract(address addr) internal view returns (bool) { 17 | uint256 size; 18 | // XXX Currently there is no better way to check if there is a contract in an address 19 | // than to check the size of the code at that address. 20 | // See https://ethereum.stackexchange.com/a/14016/36603 21 | // for more details about how this works. 22 | // TODO Check this again before the Serenity release, because all addresses will be 23 | // contracts then. 24 | assembly { size := extcodesize(addr) } // solium-disable-line security/no-inline-assembly 25 | return size > 0; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/contracts/utils/math/Math.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | 4 | /** 5 | * @title Math 6 | * @dev Assorted math operations 7 | */ 8 | library Math { 9 | function max64(uint64 a, uint64 b) internal pure returns (uint64) { 10 | return a >= b ? a : b; 11 | } 12 | 13 | function min64(uint64 a, uint64 b) internal pure returns (uint64) { 14 | return a < b ? a : b; 15 | } 16 | 17 | function max256(uint256 a, uint256 b) internal pure returns (uint256) { 18 | return a >= b ? a : b; 19 | } 20 | 21 | function min256(uint256 a, uint256 b) internal pure returns (uint256) { 22 | return a < b ? a : b; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/contracts/utils/math/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | 4 | /** 5 | * @title SafeMath 6 | * @dev Math operations with safety checks that throw on error 7 | */ 8 | library SafeMath { 9 | 10 | /** 11 | * @dev Multiplies two numbers, throws on overflow. 12 | */ 13 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 14 | if (a == 0) { 15 | return 0; 16 | } 17 | uint256 c = a * b; 18 | assert(c / a == b); 19 | return c; 20 | } 21 | 22 | /** 23 | * @dev Integer division of two numbers, truncating the quotient. 24 | */ 25 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 26 | // assert(b > 0); // Solidity automatically throws when dividing by 0 27 | // uint256 c = a / b; 28 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 29 | return a / b; 30 | } 31 | 32 | /** 33 | * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). 34 | */ 35 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 36 | assert(b <= a); 37 | return a - b; 38 | } 39 | 40 | /** 41 | * @dev Adds two numbers, throws on overflow. 42 | */ 43 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 44 | uint256 c = a + b; 45 | assert(c >= a); 46 | return c; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | var AuctionRepository = artifacts.require("./AuctionRepository.sol"); 2 | var DeedRepository = artifacts.require("./DeedRepository.sol"); 3 | // DeedRepository => 0xbb55adc67f64d1e6f08ba7523ecd2eca2ee434a3 4 | module.exports = function(deployer) { 5 | deployer.deploy(AuctionRepository); 6 | deployer.deploy(DeedRepository, "Ultra Auction NFT", "UANFT"); 7 | }; 8 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/scripts/prepare.js: -------------------------------------------------------------------------------- 1 | var deedabi = require("./build/contracts/DeedRepository.json").abi; 2 | var deedaddress = "0x454e7bd1e743ec2517a104758895bec40d9ffb9d"; 3 | var auctionabi = require("./build/contracts/AuctionRepository.json").abi; 4 | var auctionaddress = "0x14fba6349fe2f9187d08391b8dc2ca0c3742e285"; 5 | 6 | var deedinstance = web3.eth.contract(deedabi).at(deedaddress); 7 | var auctioninstance = web3.eth.contract(auctionabi).at(auctionaddress); 8 | var acc = web3.eth.accounts[0]; 9 | var ops = { from: acc, gas: 300000 }; 10 | //register a deed and transfer to auctionrepository address 11 | 12 | deedinstance.registerDeed(acc, 123456789, ops); 13 | deedinstance.approve(auctionaddress, 123456789, ops); 14 | deedinstance.transferFrom( acc ,auctionaddress, 123456789, ops ); 15 | 16 | // create auction 17 | var startprice = new web3.BigNumber(1000000000000000000) 18 | auctioninstance.createAuction(deedaddress, 123456789, "My title", "meta://", startprice, 1527811200 , ops ); 19 | 20 | auctioninstance.getAuctionsCount(ops); // should be 1 21 | 22 | auctioninstance.getAuctionById(0, ops); 23 | 24 | auctioninstance.bidOnAuction(0, { from: web3.eth.accounts[1], gas: 300000, value: new web3.BigNumber(2000000000000000000) }); // should be 1 25 | // error..cant bid on auction that is owned 26 | auctioninstance.getBidsCount(0, ops) 27 | 28 | // balance of account 2 should be 9899xxxxxx 29 | web3.eth.getBalance(web3.eth.accounts[1]).toNumber() 30 | 31 | auctioninstance.cancelAuction(0, ops); 32 | 33 | // balance back to where it was minus some gas costs 34 | web3.eth.getBalance(web3.eth.accounts[1]).toNumber() 35 | 36 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/test/1_auctionrepository.js: -------------------------------------------------------------------------------- 1 | var AuctionRepository = artifacts.require("./AuctionRepository.sol"); 2 | //var DeedRepository = artifacts.require("./DeedRepository.sol"); 3 | const fs = require('fs'); 4 | 5 | contract('AuctionRepository', async (accounts) => { 6 | 7 | it("It should check if the auction repository is initialized", async () => { 8 | let instance = await AuctionRepository.deployed(); 9 | fs.writeFileSync('./test/output.address', instance.address); 10 | let auctionLength = await instance.getAuctionsCount(); 11 | assert.equal(auctionLength.valueOf(), 0, `${auctionLength} auctions instead of 0`); 12 | }); 13 | 14 | // it("It should approve transfer of ownership of the 123456789 token", async () => { 15 | // //let instance = await DeedRepository.deployed(); 16 | // //let totalSupply = await instance.totalSupply(); 17 | 18 | // let auctionInstance = await AuctionRepository.deployed(); 19 | // let auctionAddress = auctionInstance.address; 20 | // await instance.approve(auctionAddress, 123456789); 21 | 22 | // // console.log('this auction address:', auctionAddress, ' deedrepo instance addr', instance.address); 23 | // let address = await instance.getApproved(123456789); 24 | // assert.equal(address.valueOf(), auctionAddress, `${address} should be equal to ${auctionAddress}`); 25 | // }); 26 | 27 | // it("It should transfer ownership of deed to this contract", async () => { 28 | // let instance = await DeedRepository.deployed(); 29 | // let auctionInstance = await AuctionRepository.deployed(); 30 | // let auctionAddress = auctionInstance.address; 31 | // await instance.transferFrom( accounts[0] ,auctionAddress, 123456789, { from: accounts[0]}); 32 | // let newOwnerAddress = await instance.ownerOf(123456789); 33 | // //let success = await instance.isApprovedOrOwner(auctionAddress, 123456789) 34 | // assert.equal(newOwnerAddress.valueOf(), auctionAddress, `${newOwnerAddress} should be ${auctionAddress}`); 35 | // }); 36 | 37 | // it(`It should create an auction under ${accounts[0]} account`, async () => { 38 | // let instance = await AuctionRepository.deployed(); 39 | // await instance.createAuction(accounts[0], 123456789, "MYNFT", "meta://", 10, 1533772800, accounts[0] ); 40 | // let auctionCount = await instance.getAuctionsCountOfOwner(accounts[0]); 41 | // assert.equal(auctionCount.valueOf(), 1, `auctions of ${accounts[0]} should be 1`); 42 | // }); 43 | 44 | // it(`It should bid on the last auction`, async () => { 45 | // let instance = await AuctionRepository.deployed(); 46 | // await instance.bidOnAuction(0, 1000); 47 | // let bidsCount = await instance.getBidsCount(0); 48 | // assert.equal(bidsCount.valueOf(), 1, `bids should be 1`); 49 | // }); 50 | 51 | // it(`It should bid on the last auction`, async () => { 52 | // let instance = await AuctionRepository.deployed(); 53 | // await instance.bidOnAuction(0, 10000 ); 54 | // let bidsCount = await instance.getBidsCount(0); 55 | // assert.equal(bidsCount.valueOf(), 2, `bids should be 1`); 56 | // }); 57 | 58 | 59 | }); -------------------------------------------------------------------------------- /code/auction_dapp/backend/test/2_deedrepository.js: -------------------------------------------------------------------------------- 1 | var DeedRepository = artifacts.require("./DeedRepository.sol"); 2 | const fs = require('fs'); 3 | 4 | contract('DeedRepository', async (accounts) => { 5 | 6 | let instance; 7 | let auctionContractAddress = ""; 8 | 9 | beforeEach('setup contract for each test', async function () { 10 | instance = await DeedRepository.deployed(); 11 | auctionContractAddress = fs.readFileSync("./test/output.address").toString() 12 | }) 13 | 14 | it("It should create an deed repository with UANFT as symbol", async () => { 15 | let symbol = await instance.symbol(); 16 | assert.equal(symbol.valueOf(), 'UANFT' , `Deedrepository symbol should be UANFT`); 17 | }); 18 | 19 | it("It should register a deed with id: 123456789", async () => { 20 | await instance.registerDeed(accounts[0], 123456789); 21 | let ownerOfDeed = await instance.exists(123456789); 22 | assert.equal(ownerOfDeed.valueOf(), true , `Result should be true`); 23 | }); 24 | 25 | it(`It should check owner of 123456789 who is ${accounts[0]}`, async () => { 26 | let ownerOfDeed = await instance.ownerOf(123456789); 27 | assert.equal(ownerOfDeed.valueOf(), accounts[0] , `Owner should be ${accounts[0]}`); 28 | }); 29 | 30 | it(`It should check balance of ${accounts[0]}`, async () => { 31 | let balance = await instance.balanceOf(accounts[0]); 32 | assert.equal(balance.valueOf(), 1 , `balance ${balance} should be 1`); 33 | }); 34 | 35 | it(`It should check total supply of the repository`, async () => { 36 | let supply = await instance.totalSupply(); 37 | assert.equal(supply.valueOf(), 1 , `total supply: ${supply} should be 1`); 38 | }); 39 | 40 | it(`It should approve transfer the ownership of 123456789 to the auctionRepository address`, async () => { 41 | await instance.approve(auctionContractAddress, 123456789); 42 | // console.log('this auction address:', auctionAddress, ' deedrepo instance addr', instance.address); 43 | let address = await instance.getApproved(123456789); 44 | assert.equal(address.valueOf(), auctionContractAddress, `${address} should be equal to ${auctionContractAddress}`); 45 | }); 46 | 47 | it("It should transfer ownership of deed 123456789 to this contract", async () => { 48 | await instance.transferFrom( accounts[0] ,auctionContractAddress, 123456789, { from: accounts[0]}); 49 | let newOwnerAddress = await instance.ownerOf(123456789); 50 | //let success = await instance.isApprovedOrOwner(auctionAddress, 123456789) 51 | assert.equal(newOwnerAddress.valueOf(), auctionContractAddress, `${newOwnerAddress} should be ${auctionContractAddress}`); 52 | }); 53 | 54 | 55 | }); -------------------------------------------------------------------------------- /code/auction_dapp/backend/test/output.address: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/code/auction_dapp/backend/test/output.address -------------------------------------------------------------------------------- /code/auction_dapp/backend/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | }; 4 | -------------------------------------------------------------------------------- /code/auction_dapp/backend/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | development: { 4 | host: "52.59.238.144", 5 | port: 8545, 6 | network_id: "*", 7 | gas: 3000000 8 | } 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /code/auction_dapp/frontend/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"] 12 | } 13 | -------------------------------------------------------------------------------- /code/auction_dapp/frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /code/auction_dapp/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /code/auction_dapp/frontend/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /code/auction_dapp/frontend/README.md: -------------------------------------------------------------------------------- 1 | # Auction Platform Fronten 2 | 3 | > Decentralized Auction Platform 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # build for production with minification 15 | npm run build 16 | 17 | # build for production and view the bundle analyzer report 18 | npm run build --report 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /code/auction_dapp/frontend/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /code/auction_dapp/frontend/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.2.8 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: {}, 14 | 15 | // Various Dev Server settings 16 | host: 'localhost', // can be overwritten by process.env.HOST 17 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 18 | autoOpenBrowser: false, 19 | errorOverlay: true, 20 | notifyOnErrors: true, 21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 22 | 23 | 24 | /** 25 | * Source Maps 26 | */ 27 | 28 | // https://webpack.js.org/configuration/devtool/#development 29 | devtool: 'cheap-module-eval-source-map', 30 | 31 | // If you have problems debugging vue-files in devtools, 32 | // set this to false - it *may* help 33 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 34 | cacheBusting: true, 35 | 36 | cssSourceMap: true, 37 | }, 38 | 39 | build: { 40 | // Template for index.html 41 | index: path.resolve(__dirname, '../dist/index.html'), 42 | 43 | // Paths 44 | assetsRoot: path.resolve(__dirname, '../dist'), 45 | assetsSubDirectory: 'static', 46 | assetsPublicPath: '/', 47 | 48 | /** 49 | * Source Maps 50 | */ 51 | 52 | productionSourceMap: true, 53 | // https://webpack.js.org/configuration/devtool/#production 54 | devtool: '#source-map', 55 | 56 | // Gzip off by default as many popular static hosts such as 57 | // Surge or Netlify already gzip all static assets for you. 58 | // Before setting to `true`, make sure to: 59 | // npm install --save-dev compression-webpack-plugin 60 | productionGzip: false, 61 | productionGzipExtensions: ['js', 'css'], 62 | 63 | // Run the build command with an extra argument to 64 | // View the bundle analyzer report after build finishes: 65 | // `npm run build --report` 66 | // Set to `true` or `false` to always turn it on or off 67 | bundleAnalyzerReport: process.env.npm_config_report 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /code/auction_dapp/frontend/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /code/auction_dapp/frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Auction Dapp 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 22 | -------------------------------------------------------------------------------- /code/auction_dapp/frontend/src/config.js: -------------------------------------------------------------------------------- 1 | var DeedRepository = require('./contracts/DeedRepository') 2 | var AuctionRepository = require('./contracts/AuctionRepository') 3 | 4 | module.exports = { 5 | JSONRPC_ENDPOINT: 'http://52.59.238.144:8545', 6 | JSONRPC_WS_ENDPOINT: 'ws://52.59.238.144:8546', //'ws://52.59.238.144:8546', 7 | BZZ_ENDPOINT: 'http://52.59.238.144:8500', 8 | SHH_ENDPOINT: 'ws://52.59.238.144:8546', 9 | 10 | DEEDREPOSITORY_ADDRESS: '0xfc35c45cd57661197d0bb19399c9d3ede1c50dcc', 11 | AUCTIONREPOSITORY_ADDRESS: '0xefbebbf64a570f7b94f168430f45ecbb87546f06', 12 | 13 | DEEDREPOSITORY_ABI: DeedRepository.abi, 14 | AUCTIONREPOSITORY_ABI: AuctionRepository.abi, 15 | 16 | GAS_AMOUNT: 500000, 17 | 18 | //whisper settings 19 | WHISPER_SHARED_KEY: '0x8bda3abeb454847b515fa9b404cede50b1cc63cfdeddd4999d074284b4c21e15' 20 | 21 | } 22 | 23 | // web3.eth.sendTransaction({from: web3.eth.accounts[0], to: "0x6f0023D1CFe5A7A56F96e61E0169B775Ac97f90E" , value: web3.toWei(1, 'ether'), gasLimit: 21000, gasPrice: 20000000000}) 24 | -------------------------------------------------------------------------------- /code/auction_dapp/frontend/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App' 3 | import Config from './config' 4 | import router from './router' 5 | import Vuetify from 'vuetify' 6 | import VueResource from 'vue-resource' 7 | import { ChatRoom } from './models/ChatRoom' 8 | import { DeedRepository } from './models/DeedRepository' 9 | import { AuctionRepository } from './models/AuctionRepository' 10 | 11 | // rename to avoid conflict between metamask 12 | // will be used for whisper v5/6 13 | var Web3_1 = require('web3') 14 | 15 | Vue.use(VueResource) 16 | Vue.use(Vuetify) 17 | Vue.config.productionTip = false 18 | 19 | // state management 20 | var store = { 21 | debug: true, 22 | state: { 23 | // metamask state variable 24 | metamask: { 25 | web3DefaultAccount: '', 26 | metamaskInstalled: false, 27 | networkId: '', 28 | }, 29 | 30 | // local web3 instance(not metamask) 31 | web3 : null, 32 | 33 | }, 34 | networkReady() { 35 | return this.getNetworkId() != '' && this.getMetamaskInstalled() && this.getWeb3DefaultAccount() != '' 36 | }, 37 | setNetworkId(networkId) { 38 | this.state.metamask.networkId = networkId 39 | }, 40 | getNetworkId() { 41 | return this.state.metamask.networkId 42 | }, 43 | 44 | setWeb3DefaultAccount(account) { 45 | this.state.metamask.web3DefaultAccount = account 46 | }, 47 | getWeb3DefaultAccount() { 48 | return this.state.metamask.web3DefaultAccount 49 | }, 50 | 51 | setMetamaskInstalled(){ 52 | this.state.metamask.metamaskInstalled = true 53 | }, 54 | getMetamaskInstalled(){ 55 | return this.state.metamask.metamaskInstalled 56 | }, 57 | 58 | getRandomInt(min, max) { 59 | return Math.floor(Math.random() * (max - min + 1)) + min 60 | } 61 | } 62 | 63 | 64 | Vue.mixin({ 65 | created: function () { 66 | // Inject the models to components 67 | this.$chatroomInstance = new ChatRoom() 68 | this.$deedRepoInstance = new DeedRepository() 69 | this.$auctionRepoInstance = new AuctionRepository() 70 | 71 | this.$chatroomInstance.setWeb3(new Web3_1(Config.SHH_ENDPOINT)) 72 | 73 | // one instance of web3 available to all components 74 | if (typeof web3 !== 'undefined') { 75 | web3 = new Web3(web3.currentProvider) 76 | this.$auctionRepoInstance.setWeb3(web3) 77 | this.$deedRepoInstance.setWeb3(web3) 78 | 79 | store.setMetamaskInstalled() 80 | web3.version.getNetwork((err, netId) => { 81 | store.setNetworkId(netId) 82 | }) 83 | // pull accounts every 2 seconds 84 | setInterval(() => { 85 | web3.eth.getAccounts((err, data) => { 86 | if(data.length > 0) store.setWeb3DefaultAccount(data[0]) 87 | }) 88 | }, 2000) 89 | } 90 | // inject config to components 91 | this.$config = Config 92 | } 93 | }) 94 | 95 | 96 | new Vue({ 97 | el: '#app', 98 | data: { 99 | globalState: store, 100 | }, 101 | router, 102 | template: '', 103 | components: { App }, 104 | mounted() { 105 | 106 | } 107 | }) 108 | -------------------------------------------------------------------------------- /code/auction_dapp/frontend/src/models/AuctionRepository.js: -------------------------------------------------------------------------------- 1 | import Config from '../config' 2 | 3 | export class AuctionRepository { 4 | 5 | web3 = null 6 | account = '' 7 | contractInstance = null 8 | gas = 4476768 9 | 10 | constructor(){ 11 | this.gas = Config.GAS_AMOUNT 12 | } 13 | 14 | setWeb3(web3) { 15 | this.web3 = web3 16 | this.contractInstance = this.web3.eth.contract(Config.AUCTIONREPOSITORY_ABI).at(Config.AUCTIONREPOSITORY_ADDRESS) 17 | } 18 | 19 | getWeb3() { 20 | return this.web3 21 | } 22 | 23 | setAccount(account){ 24 | this.account = account 25 | } 26 | 27 | 28 | getCurrentBlock() { 29 | return new Promise((resolve, reject ) => { 30 | this.web3.eth.getBlockNumber((err, blocknumber) => { 31 | if(!err) resolve(blocknumber) 32 | reject(err) 33 | }) 34 | }) 35 | } 36 | 37 | async watchIfCreated(cb) { 38 | const currentBlock = await this.getCurrentBlock() 39 | const eventWatcher = this.contractInstance.AuctionCreated({}, {fromBlock: currentBlock - 1, toBlock: 'latest'}) 40 | eventWatcher.watch(cb) 41 | } 42 | 43 | async watchIfBidSuccess(cb) { 44 | const currentBlock = await this.getCurrentBlock() 45 | const eventWatcher = this.contractInstance.BidSuccess({}, {fromBlock: currentBlock - 1, toBlock: 'latest'}) 46 | eventWatcher.watch(cb) 47 | } 48 | 49 | async watchIfCanceled(cb) { 50 | const currentBlock = await this.getCurrentBlock() 51 | const eventWatcher = this.contractInstance.AuctionCanceled({}, {fromBlock: currentBlock - 1, toBlock: 'latest'}) 52 | eventWatcher.watch(cb) 53 | } 54 | 55 | async watchIfFinalized(cb) { 56 | const currentBlock = await this.getCurrentBlock() 57 | const eventWatcher = this.contractInstance.AuctionFinalized({}, {fromBlock: currentBlock - 1, toBlock: 'latest'}) 58 | eventWatcher.watch(cb) 59 | } 60 | getCurrentBid(auctionId) { 61 | return new Promise(async (resolve, reject) => { 62 | try { 63 | this.contractInstance.getCurrentBid(auctionId, {from: this.account, gas: this.gas }, (err, transaction) => { 64 | if(!err) resolve(transaction) 65 | reject(err) 66 | }) 67 | } catch(e) { 68 | reject(e) 69 | } 70 | }) 71 | } 72 | 73 | getBidCount(auctionId) { 74 | return new Promise(async (resolve, reject) => { 75 | try { 76 | this.contractInstance.getBidsCount(auctionId, {from: this.account, gas: this.gas }, (err, transaction) => { 77 | if(!err) resolve(transaction) 78 | reject(err) 79 | }) 80 | } catch(e) { 81 | reject(e) 82 | } 83 | }) 84 | } 85 | 86 | getCount() { 87 | return new Promise(async (resolve, reject) => { 88 | try { 89 | this.contractInstance.getCount({from: this.account, gas: this.gas }, (err, transaction) => { 90 | if(!err) resolve(transaction) 91 | reject(err) 92 | }) 93 | } catch(e) { 94 | reject(e) 95 | } 96 | }) 97 | } 98 | 99 | bid(auctionId, price) { 100 | console.log(auctionId, this.web3.toWei(price, 'ether')) 101 | return new Promise(async (resolve, reject) => { 102 | try { 103 | this.contractInstance.bidOnAuction(auctionId, {from: this.account, gas: this.gas, value: this.web3.toWei(price, 'ether') }, (err, transaction) => { 104 | if(!err) resolve(transaction) 105 | reject(err) 106 | }) 107 | } catch(e) { 108 | reject(e) 109 | } 110 | }) 111 | } 112 | 113 | create(deedId, auctionTitle, metadata, startingPrice, blockDeadline) { 114 | return new Promise(async (resolve, reject) => { 115 | try { 116 | 117 | this.contractInstance.createAuction(Config.DEEDREPOSITORY_ADDRESS, deedId, auctionTitle, metadata, this.web3.toWei(startingPrice, 'ether'), blockDeadline, {from: this.account, gas: this.gas }, (err, transaction) => { 118 | if(!err) resolve(transaction) 119 | reject(err) 120 | }) 121 | } catch(e) { 122 | reject(e) 123 | } 124 | }) 125 | } 126 | 127 | cancel(auctionId) { 128 | return new Promise(async (resolve, reject) => { 129 | try { 130 | this.contractInstance.cancelAuction(auctionId, {from: this.account, gas: this.gas }, (err, transaction) => { 131 | if(!err) resolve(transaction) 132 | reject(err) 133 | }) 134 | } catch(e) { 135 | reject(e) 136 | } 137 | }) 138 | } 139 | 140 | finalize(auctionId) { 141 | return new Promise(async (resolve, reject) => { 142 | try { 143 | this.contractInstance.finalizeAuction(auctionId, {from: this.account, gas: this.gas }, (err, transaction) => { 144 | if(!err) resolve(transaction) 145 | reject(err) 146 | }) 147 | } catch(e) { 148 | reject(e) 149 | } 150 | }) 151 | } 152 | 153 | findById(auctionId) { 154 | return new Promise(async (resolve, reject) => { 155 | try { 156 | this.contractInstance.getAuctionById(auctionId, { from: this.account, gas: this.gas }, (err, transaction) => { 157 | if(!err) resolve(transaction) 158 | reject(err) 159 | }) 160 | } catch(e) { 161 | reject(e) 162 | } 163 | }) 164 | } 165 | 166 | } -------------------------------------------------------------------------------- /code/auction_dapp/frontend/src/models/ChatRoom.js: -------------------------------------------------------------------------------- 1 | import Config from '../config' 2 | 3 | export class ChatRoom { 4 | web3 = null 5 | keyID = '' 6 | privateKey = '' 7 | publicKey = '' 8 | conn = null 9 | room = '' 10 | identity = '' 11 | 12 | constructor(){ 13 | this.privateKey = Config.WHISPER_SHARED_KEY 14 | } 15 | 16 | async setWeb3(web3){ 17 | this.web3 = web3 18 | this.keyID = await this.web3.shh.addPrivateKey(this.privateKey) 19 | this.publicKey = await this.web3.shh.getPublicKey(this.keyID) 20 | } 21 | 22 | getWeb3(){ 23 | return this.web3 24 | } 25 | 26 | async getWeb3ConnStatusAction() { 27 | return await this.web3.shh.net.isListening() 28 | } 29 | 30 | async subscribeToTopic(topic, fn) { 31 | this.conn = this.web3.shh.subscribe("messages", { 32 | privateKeyID: this.keyID, 33 | //topics: [this.web3.utils.stringToHex(topic)] 34 | topics: [topic] 35 | }).on('data', fn) 36 | } 37 | 38 | async sendJoinEvent(identity, room, data) { 39 | const messageRaw = { 40 | identity: identity, 41 | type: "join", 42 | data: data 43 | } 44 | return await this.sendRawWhisperMessage(messageRaw, room ) 45 | } 46 | 47 | async sendMessageEvent(identity, room, data) { 48 | const messageRaw = { 49 | identity: identity, 50 | type: "msg", 51 | data: data 52 | } 53 | return await this.sendRawWhisperMessage(messageRaw, room ) 54 | } 55 | 56 | async sendRawWhisperMessage(msg, topic){ 57 | return await this.web3.shh.post({ 58 | pubKey: this.publicKey, // encrypts using the sym key ID 59 | ttl: 10, 60 | //topic: this.web3.utils.stringToHex(topic), 61 | topic: topic, 62 | payload: this.web3.utils.stringToHex(JSON.stringify(msg)), 63 | powTime: 60, 64 | powTarget: 0.2 65 | }) 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /code/auction_dapp/frontend/src/models/DeedRepository.js: -------------------------------------------------------------------------------- 1 | import Config from '../config' 2 | 3 | export class DeedRepository { 4 | 5 | web3 = null 6 | account = '' 7 | contractInstance = null 8 | gas = 4476768 9 | 10 | constructor(){ 11 | this.gas = Config.GAS_AMOUNT 12 | } 13 | setWeb3(web3) { 14 | this.web3 = web3 15 | this.contractInstance = this.web3.eth.contract(Config.DEEDREPOSITORY_ABI).at(Config.DEEDREPOSITORY_ADDRESS) 16 | } 17 | 18 | getWeb3() { 19 | return this.web3 20 | } 21 | 22 | setAccount(account){ 23 | this.account = account 24 | } 25 | 26 | getCurrentBlock() { 27 | return new Promise((resolve, reject ) => { 28 | this.web3.eth.getBlockNumber((err, blocknumber) => { 29 | if(!err) resolve(blocknumber) 30 | reject(err) 31 | }) 32 | }) 33 | } 34 | 35 | // getAccounts() { 36 | // return new Promise((resolve, reject ) => { 37 | // this.web3.eth.getAccounts((err, accounts) => { 38 | // if(!err) resolve(accounts) 39 | // reject(err) 40 | // }) 41 | // }) 42 | // } 43 | 44 | async watchIfCreated(cb) { 45 | const currentBlock = await this.getCurrentBlock() 46 | const eventWatcher = this.contractInstance.DeedRegistered({}, {fromBlock: currentBlock - 1, toBlock: 'latest'}) 47 | eventWatcher.watch(cb) 48 | } 49 | 50 | async watchIfDeedTransfered(cb) { 51 | const currentBlock = await this.getCurrentBlock() 52 | const eventWatcher = this.contractInstance.Transfer({}, {fromBlock: currentBlock - 1, toBlock: 'latest'}) 53 | eventWatcher.watch(cb) 54 | } 55 | 56 | exists(deedId) { 57 | return new Promise(async (resolve, reject) => { 58 | this.contractInstance.exists(deedId, {from: this.account, gas: this.gas }, (err, transaction) => { 59 | if(!err) resolve(transaction) 60 | reject(err) 61 | }) 62 | }) 63 | } 64 | 65 | transferTo(to, deedId) { 66 | return new Promise(async (resolve, reject) => { 67 | this.contractInstance.transferFrom(this.account, to, deedId, {from: this.account, gas: this.gas }, (err, transaction) => { 68 | if(!err) resolve(transaction) 69 | reject(err) 70 | }) 71 | }) 72 | 73 | } 74 | 75 | create(deedId, deedURI) { 76 | console.log('contractinsatnce', this.contractInstance ) 77 | return new Promise(async (resolve, reject) => { 78 | this.contractInstance.registerDeed(deedId, deedURI, {from: this.account, gas: this.gas }, (err, transaction) => { 79 | if(!err) 80 | resolve(transaction) 81 | else 82 | reject(err) 83 | }) 84 | }) 85 | } 86 | } -------------------------------------------------------------------------------- /code/auction_dapp/frontend/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Home from '@/components/Home' 4 | import Auction from '@/components/Auction' 5 | 6 | Vue.use(Router) 7 | 8 | export default new Router({ 9 | routes: [ 10 | { 11 | path: '/', 12 | name: 'Home', 13 | component: Home 14 | }, 15 | { 16 | path: '/auctions/:id', 17 | name: 'Auction', 18 | component: Auction 19 | } 20 | ], 21 | mode: 'history', 22 | scrollBehavior (to, from, savedPosition) { 23 | return { x: 0, y: 0 } 24 | } 25 | }) 26 | -------------------------------------------------------------------------------- /code/auction_dapp/frontend/static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/code/auction_dapp/frontend/static/.gitkeep -------------------------------------------------------------------------------- /code/aws/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "chainId": 15, 4 | "homesteadBlock": 0, 5 | "eip155Block": 0, 6 | "eip158Block": 0 7 | }, 8 | "nonce": "0x0000000000000042", 9 | "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", 10 | "difficulty": "0x4000", 11 | "alloc": {}, 12 | "coinbase": "0x0000000000000000000000000000000000000000", 13 | "timestamp": "0x00", 14 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 15 | "gasLimit": "0xffffffff" 16 | } -------------------------------------------------------------------------------- /code/aws/truffle.js: -------------------------------------------------------------------------------- 1 | // Install dependencies: 2 | // npm init 3 | // npm install --save-dev dotenv truffle-wallet-provider ethereumjs-wallet 4 | 5 | // Create .env in project root, with keys: 6 | // ROPSTEN_PRIVATE_KEY="123abc" 7 | // MAINNET_PRIVATE_KEY="123abc" 8 | 9 | require('dotenv').config(); 10 | const Web3 = require("web3"); 11 | const web3 = new Web3(); 12 | const WalletProvider = require("truffle-wallet-provider"); 13 | const Wallet = require('ethereumjs-wallet'); 14 | 15 | var mainNetPrivateKey = new Buffer(process.env["MAINNET_PRIVATE_KEY"], "hex") 16 | var mainNetWallet = Wallet.fromPrivateKey(mainNetPrivateKey); 17 | var mainNetProvider = new WalletProvider(mainNetWallet, "https://mainnet.infura.io/"); 18 | 19 | var ropstenPrivateKey = new Buffer(process.env["ROPSTEN_PRIVATE_KEY"], "hex") 20 | var ropstenWallet = Wallet.fromPrivateKey(ropstenPrivateKey); 21 | var ropstenProvider = new WalletProvider(ropstenWallet, "https://ropsten.infura.io/"); 22 | 23 | module.exports = { 24 | networks: { 25 | dev: { // Whatever network our local node connects to 26 | network_id: "*", // Match any network id 27 | host: "localhost", 28 | port: 8545 29 | }, 30 | mainnet: { // Provided by Infura, load keys in .env file 31 | network_id: "1", 32 | provider: mainNetProvider, 33 | gas: 4600000, 34 | gasPrice: web3.toWei("20", "gwei") 35 | }, 36 | ropsten: { // Provided by Infura, load keys in .env file 37 | network_id: "3", 38 | provider: ropstenProvider, 39 | gas: 4600000, 40 | gasPrice: web3.toWei("20", "gwei") 41 | }, 42 | kovan: { 43 | network_id: 42, 44 | host: "localhost", // parity --chain=kovan 45 | port: 8545, 46 | gas: 5000000 47 | }, 48 | ganache: { // Ganache local test RPC blockchain 49 | network_id: "5777", 50 | host: "localhost", 51 | port: 7545, 52 | gas: 6721975 53 | }, 54 | aws: { // Private network on AWS 55 | network_id: "15", 56 | host: "public-ip-address", 57 | port: 8545, 58 | gas: 6721975 59 | } 60 | } 61 | }; -------------------------------------------------------------------------------- /code/jsonrpc/http/js/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /code/jsonrpc/http/js/README.md: -------------------------------------------------------------------------------- 1 | # Ethereum HTTP Client 2 | 3 | Connect to a node using HTTP(POST) and call the following rpc: 4 | - Get accounts associated with a node(Only you should be able to access these endpoint) 5 | - Get the current block number 6 | 7 | 8 | ## Build Setup 9 | 10 | ``` bash 11 | # install dependencies 12 | npm install 13 | 14 | # run the node script 15 | npm run http 16 | 17 | ``` 18 | -------------------------------------------------------------------------------- /code/jsonrpc/http/js/index.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios") 2 | const url = "http://172.16.163.129:8545" 3 | 4 | const requests = [ 5 | { 6 | // Get the current block number 7 | jsonrpc: "2.0", 8 | method: "eth_blockNumber", 9 | params: [], 10 | id: 1234 11 | }, 12 | { 13 | // Get accounts on this node 14 | jsonrpc: "2.0", 15 | method: "eth_accounts", 16 | params: [], 17 | id: 12345 18 | } 19 | ] 20 | 21 | requests.map(async (request) => { 22 | try { 23 | // send the request 24 | console.log("Call procedure: ", request.method) 25 | const result = await axios.post(url, request) 26 | 27 | // check for errors 28 | if(result.data.error) 29 | console.log("Error: ", result.data.error.message) 30 | else 31 | console.log("Ethereum node response: ", result.data) 32 | } catch (e) { 33 | console.log("Error while connecting to the remote server", e) 34 | } 35 | 36 | }) -------------------------------------------------------------------------------- /code/jsonrpc/ipc/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "log" 8 | "net" 9 | "sync" 10 | ) 11 | 12 | // Request represents the jsonrpc2 request 13 | type Request struct { 14 | Jsonrpc string `json:"jsonrpc"` 15 | Method string `json:"method"` 16 | Params interface{} `json:"params"` 17 | ID uint64 `json:"id"` 18 | } 19 | 20 | // ReadIPC reads from a socket 21 | func ReadIPC(r io.Reader, wg *sync.WaitGroup) { 22 | //20KB buffer 23 | buf := make([]byte, 20*1024) 24 | n, err := r.Read(buf[:]) 25 | if err != nil { 26 | log.Fatal("Unable to read from socket", err) 27 | } 28 | fmt.Println("Ethereum node response: ", string(buf[0:n])) 29 | wg.Done() 30 | } 31 | 32 | func main() { 33 | var wg sync.WaitGroup 34 | socketfile := "/home/younix/testnet/chaindata/geth.ipc" 35 | 36 | // create a request 37 | params := make([]int, 0) 38 | request := &Request{ 39 | Jsonrpc: "2.0", 40 | Method: "eth_blockNumber", 41 | Params: params, 42 | ID: 12345, 43 | } 44 | 45 | // marshal the struct to json string 46 | strRequest, err := json.Marshal(&request) 47 | if err != nil { 48 | log.Fatal("Unable to encode request", err) 49 | } 50 | 51 | // dial the socket 52 | ipc, err := net.Dial("unix", socketfile) 53 | if err != nil { 54 | log.Fatal("Unable to connect to the unix socket", err) 55 | } 56 | defer ipc.Close() 57 | 58 | // listen for incoming messages 59 | wg.Add(1) 60 | go ReadIPC(ipc, &wg) 61 | 62 | // write to the unix socket 63 | fmt.Println("Call procedure: ", request.Method) 64 | _, err = ipc.Write(strRequest) 65 | if err != nil { 66 | log.Fatal("Unable to write to the unix socket", err) 67 | } 68 | 69 | // wait for response 70 | wg.Wait() 71 | } 72 | -------------------------------------------------------------------------------- /code/jsonrpc/websockets/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /code/jsonrpc/websockets/README.md: -------------------------------------------------------------------------------- 1 | # Ethereum WS Client 2 | 3 | Connect to a node using Websockets and call the following rpc: 4 | - Get accounts associated with a node(Only you should be able to access these endpoint) 5 | - Get the current block number 6 | 7 | 8 | ## Build Setup 9 | 10 | ``` bash 11 | # install dependencies 12 | npm install 13 | 14 | # run the node script 15 | npm run rpc 16 | 17 | ``` 18 | -------------------------------------------------------------------------------- /code/jsonrpc/websockets/app.js: -------------------------------------------------------------------------------- 1 | const WebSocket = require('ws'); 2 | const wsEndpoint = "ws://172.16.163.129:8546" 3 | const ws = new WebSocket(wsEndpoint); 4 | 5 | const requests = [ 6 | { 7 | // Get the current block number 8 | jsonrpc: "2.0", 9 | method: "eth_blockNumber", 10 | params: [], 11 | id: 1234 12 | }, 13 | { 14 | // Get accounts on this node 15 | jsonrpc: "2.0", 16 | method: "eth_accounts", 17 | params: [], 18 | id: 12345 19 | } 20 | ] 21 | 22 | // Connection is established 23 | ws.on('open', function open() { 24 | requests.map((req) => { 25 | console.log("Call procedure: ", req.method) 26 | ws.send(JSON.stringify(req)) 27 | }) 28 | }); 29 | 30 | // Listen for incoming messages 31 | ws.on('message', function incoming(data) { 32 | console.log("Ethereum node response: ", JSON.parse(data)); 33 | }); 34 | -------------------------------------------------------------------------------- /code/jupyter_notebook/.ipynb_checkpoints/keys_and_addresses_python_notebook-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Keys and Addresses Exercises" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "### Requirements\n", 15 | "\n", 16 | " $ pip3 install ethereum\n", 17 | " \n", 18 | " $ pip3 install bitcoin\n", 19 | " \n", 20 | " $ pip3 install pycryptodomex\n", 21 | " \n", 22 | " $ pip3 install jupyter\n" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 35, 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "# Import libraries\n", 32 | "\n", 33 | "\n", 34 | "# Vitalik Buterin's Python Library for bitcoin\n", 35 | "# No longer maintained!\n", 36 | "# https://pypi.python.org/pypi/bitcoin/1.1.42\n", 37 | "import bitcoin\n", 38 | "\n", 39 | "# Vitalik Buterin's Python Library for Ethereum\n", 40 | "# https://github.com/ethereum/pyethereum\n", 41 | "import ethereum\n", 42 | "\n", 43 | "# Wrong source of SHA3 (FIPS-202 not Keccak-256)\n", 44 | "from hashlib import sha3_256 as hashlib_sha3\n", 45 | "\n", 46 | "# Both FIP-202 SHA-3 and Keccak-256 from pycryptodomex\n", 47 | "from Crypto.Hash import SHA3_256 as crypto_sha3\n", 48 | "from Crypto.Hash import keccak as crypto_keccak\n", 49 | "\n", 50 | "# Ethereum library implements Keccak, but calls it sha3\n", 51 | "from ethereum.utils import sha3 as ethereum_sha3\n", 52 | "\n", 53 | "from rlp.utils import decode_hex, encode_hex\n" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 36, 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "privkey_hex = \"f8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315\"" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 37, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "privkey = decode_hex(privkey_hex)" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 38, 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [ 80 | "# Use pybitcointools (bitcoin) library's elliptic curve functions to calculate the public key\n", 81 | "\n", 82 | "pubkey = bitcoin.privtopub(privkey)" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 39, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "pubkey_hex = encode_hex(pubkey)" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 40, 97 | "metadata": {}, 98 | "outputs": [ 99 | { 100 | "name": "stdout", 101 | "output_type": "stream", 102 | "text": [ 103 | "Public Key: 046e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683bae07b83b5c38e5e2b0c8529d7fa3f64d46daa1ece2d9ac14cab9477d042c84c32ccd0\n" 104 | ] 105 | } 106 | ], 107 | "source": [ 108 | "print(\"Public Key: \" + pubkey_hex)" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 41, 114 | "metadata": {}, 115 | "outputs": [ 116 | { 117 | "name": "stdout", 118 | "output_type": "stream", 119 | "text": [ 120 | "x (hex) : 6e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683bae07b\n", 121 | "y (hex) : 83b5c38e5e2b0c8529d7fa3f64d46daa1ece2d9ac14cab9477d042c84c32ccd0\n", 122 | "x (int) : 49790390825249384486033144355916864607616083520101638681403973749255924539515\n", 123 | "y (int) : 59574132161899900045862086493921015780032175291755807399284007721050341297360\n" 124 | ] 125 | } 126 | ], 127 | "source": [ 128 | "pubkey_without_prefix = pubkey_hex[2:]\n", 129 | "x_hex = pubkey_without_prefix[:64]\n", 130 | "y_hex = pubkey_without_prefix[64:]\n", 131 | "print(\"x (hex) : \" + x_hex)\n", 132 | "print(\"y (hex) : \" + y_hex)\n", 133 | "\n", 134 | "x = int(x_hex, 16)\n", 135 | "y = int(y_hex, 16)\n", 136 | "print(\"x (int) : \", x)\n", 137 | "print(\"y (int) : \", y)" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 42, 143 | "metadata": {}, 144 | "outputs": [ 145 | { 146 | "data": { 147 | "text/plain": [ 148 | "0" 149 | ] 150 | }, 151 | "execution_count": 42, 152 | "metadata": {}, 153 | "output_type": "execute_result" 154 | } 155 | ], 156 | "source": [ 157 | "# Prove pubkey is a point on the curve\n", 158 | "\n", 159 | "# p is the prime order of the elliptic curve field\n", 160 | "p = 115792089237316195423570985008687907853269984665640564039457584007908834671663\n", 161 | "\n", 162 | "(x ** 3 + 7 - y**2) % p" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": 50, 168 | "metadata": {}, 169 | "outputs": [ 170 | { 171 | "name": "stdout", 172 | "output_type": "stream", 173 | "text": [ 174 | "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\n", 175 | "Hash Function is Keccak-256\n" 176 | ] 177 | } 178 | ], 179 | "source": [ 180 | "# Which \"SHA3\" am I using?\n", 181 | "\n", 182 | "# Uncomment below to try various options\n", 183 | "#test_hash = hashlib_sha3(b\"\").hexdigest()\n", 184 | "#test_hash = crypto_sha3.new(b\"\").hexdigest()\n", 185 | "#test_hash = crypto_keccak.new(digest_bits=256, data=b\"\").hexdigest()\n", 186 | "#test_hash = encode_hex(ethereum_sha3(b\"\"))\n", 187 | "\n", 188 | "\n", 189 | "print(test_hash)\n", 190 | "\n", 191 | "if test_hash == \"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\":\n", 192 | " print(\"Hash Function is Keccak-256\")\n", 193 | "elif test_hash == \"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a\":\n", 194 | " print(\"Hash Function is FIP-202 SHA-3\")\n", 195 | "else: \n", 196 | " print(\"Oops! Can't identify SHA3\")\n", 197 | "\n" 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": 51, 203 | "metadata": {}, 204 | "outputs": [ 205 | { 206 | "name": "stdout", 207 | "output_type": "stream", 208 | "text": [ 209 | "Public Key Hash: 2a5bc342ed616b5ba5732269001d3f1ef827552ae1114027bd3ecf1f086ba0f9\n" 210 | ] 211 | } 212 | ], 213 | "source": [ 214 | "hex_hash = encode_hex(ethereum_sha3(decode_hex(pubkey_without_prefix)))\n", 215 | "print (\"Public Key Hash: \" + hex_hash)" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": 48, 221 | "metadata": {}, 222 | "outputs": [ 223 | { 224 | "name": "stdout", 225 | "output_type": "stream", 226 | "text": [ 227 | "Ethereum Address: 0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9\n" 228 | ] 229 | } 230 | ], 231 | "source": [ 232 | "print(\"Ethereum Address: 0x\" + hex_hash[24:])" 233 | ] 234 | } 235 | ], 236 | "metadata": { 237 | "kernelspec": { 238 | "display_name": "Python 3", 239 | "language": "python", 240 | "name": "python3" 241 | }, 242 | "language_info": { 243 | "codemirror_mode": { 244 | "name": "ipython", 245 | "version": 3 246 | }, 247 | "file_extension": ".py", 248 | "mimetype": "text/x-python", 249 | "name": "python", 250 | "nbconvert_exporter": "python", 251 | "pygments_lexer": "ipython3", 252 | "version": "3.6.3" 253 | } 254 | }, 255 | "nbformat": 4, 256 | "nbformat_minor": 2 257 | } 258 | -------------------------------------------------------------------------------- /code/jupyter_notebook/keys_and_addresses_python_notebook.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Keys and Addresses Exercises" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "### Requirements\n", 15 | "\n", 16 | " $ pip3 install ethereum\n", 17 | " \n", 18 | " $ pip3 install bitcoin\n", 19 | " \n", 20 | " $ pip3 install pycryptodomex\n", 21 | " \n", 22 | " $ pip3 install jupyter\n" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 13, 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "# Import libraries\n", 32 | "\n", 33 | "import sys\n", 34 | "\n", 35 | "# Vitalik Buterin's Python Library for Bitcoin\n", 36 | "# No longer maintained!\n", 37 | "# https://pypi.python.org/pypi/bitcoin/1.1.42\n", 38 | "import bitcoin\n", 39 | "\n", 40 | "# Vitalik Buterin's Python Library for Ethereum\n", 41 | "# https://github.com/ethereum/pyethereum\n", 42 | "import ethereum\n", 43 | "\n", 44 | "# pysha3 package - SHA-3 (Keccak) for Python 2.7 - 3.5\n", 45 | "# The sha3 module monkey patches the hashlib module.\n", 46 | "# The monkey patch is automatically activated with the first import of the sha3 module.\n", 47 | "if sys.version_info < (3, 6):\n", 48 | " import sha3\n", 49 | "\n", 50 | "# Wrong source of SHA3 (FIPS-202 not Keccak-256)\n", 51 | "from hashlib import sha3_256 as hashlib_sha3\n", 52 | "\n", 53 | "# Both FIP-202 SHA-3 and Keccak-256 from pycryptodomex\n", 54 | "from Crypto.Hash import SHA3_256 as crypto_sha3\n", 55 | "from Crypto.Hash import keccak as crypto_keccak\n", 56 | "\n", 57 | "# Ethereum library implements Keccak, but calls it sha3\n", 58 | "from ethereum.utils import sha3 as keccak256\n", 59 | "\n", 60 | "from rlp.utils import decode_hex, encode_hex\n" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 14, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "privkey_hex = \"f8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315\"" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 15, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "privkey = decode_hex(privkey_hex)" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 16, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "# Use pybitcointools (bitcoin) library's elliptic curve functions to calculate the public key\n", 88 | "\n", 89 | "pubkey = bitcoin.privtopub(privkey)" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": 17, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "pubkey_hex = encode_hex(pubkey)" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": 18, 104 | "metadata": {}, 105 | "outputs": [ 106 | { 107 | "name": "stdout", 108 | "output_type": "stream", 109 | "text": [ 110 | "Public Key: 046e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683bae07b83b5c38e5e2b0c8529d7fa3f64d46daa1ece2d9ac14cab9477d042c84c32ccd0\n" 111 | ] 112 | } 113 | ], 114 | "source": [ 115 | "print(\"Public Key: \" + pubkey_hex)" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 19, 121 | "metadata": {}, 122 | "outputs": [ 123 | { 124 | "name": "stdout", 125 | "output_type": "stream", 126 | "text": [ 127 | "x (hex) : 6e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683bae07b\n", 128 | "y (hex) : 83b5c38e5e2b0c8529d7fa3f64d46daa1ece2d9ac14cab9477d042c84c32ccd0\n", 129 | "x (int) : 49790390825249384486033144355916864607616083520101638681403973749255924539515\n", 130 | "y (int) : 59574132161899900045862086493921015780032175291755807399284007721050341297360\n" 131 | ] 132 | } 133 | ], 134 | "source": [ 135 | "pubkey_without_prefix = pubkey_hex[2:]\n", 136 | "x_hex = pubkey_without_prefix[:64]\n", 137 | "y_hex = pubkey_without_prefix[64:]\n", 138 | "print(\"x (hex) : \" + x_hex)\n", 139 | "print(\"y (hex) : \" + y_hex)\n", 140 | "\n", 141 | "x = int(x_hex, 16)\n", 142 | "y = int(y_hex, 16)\n", 143 | "print(\"x (int) : \", x)\n", 144 | "print(\"y (int) : \", y)" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 20, 150 | "metadata": {}, 151 | "outputs": [ 152 | { 153 | "data": { 154 | "text/plain": [ 155 | "0" 156 | ] 157 | }, 158 | "execution_count": 20, 159 | "metadata": {}, 160 | "output_type": "execute_result" 161 | } 162 | ], 163 | "source": [ 164 | "# Prove pubkey is a point on the curve\n", 165 | "\n", 166 | "# p is the prime order of the elliptic curve field\n", 167 | "p = 115792089237316195423570985008687907853269984665640564039457584007908834671663\n", 168 | "\n", 169 | "(x ** 3 + 7 - y**2) % p" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": 21, 175 | "metadata": {}, 176 | "outputs": [ 177 | { 178 | "name": "stdout", 179 | "output_type": "stream", 180 | "text": [ 181 | "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\n", 182 | "Hash Function is Keccak-256\n" 183 | ] 184 | } 185 | ], 186 | "source": [ 187 | "# Which \"SHA3\" am I using?\n", 188 | "\n", 189 | "# Uncomment below to try various options\n", 190 | "#test_hash = hashlib_sha3(b\"\").hexdigest()\n", 191 | "#test_hash = crypto_sha3.new(b\"\").hexdigest()\n", 192 | "#test_hash = crypto_keccak.new(digest_bits=256, data=b\"\").hexdigest()\n", 193 | "test_hash = encode_hex(keccak256(b\"\"))\n", 194 | "\n", 195 | "\n", 196 | "print(test_hash)\n", 197 | "\n", 198 | "if test_hash == \"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\":\n", 199 | " print(\"Hash Function is Keccak-256\")\n", 200 | "elif test_hash == \"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a\":\n", 201 | " print(\"Hash Function is FIP-202 SHA-3\")\n", 202 | "else: \n", 203 | " print(\"Oops! Can't identify SHA3\")\n", 204 | "\n" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": 22, 210 | "metadata": {}, 211 | "outputs": [ 212 | { 213 | "name": "stdout", 214 | "output_type": "stream", 215 | "text": [ 216 | "Public Key Hash: 2a5bc342ed616b5ba5732269001d3f1ef827552ae1114027bd3ecf1f086ba0f9\n" 217 | ] 218 | } 219 | ], 220 | "source": [ 221 | "hex_hash = encode_hex(keccak256(decode_hex(pubkey_without_prefix)))\n", 222 | "print (\"Public Key Hash: \" + hex_hash)" 223 | ] 224 | }, 225 | { 226 | "cell_type": "code", 227 | "execution_count": 23, 228 | "metadata": {}, 229 | "outputs": [ 230 | { 231 | "name": "stdout", 232 | "output_type": "stream", 233 | "text": [ 234 | "Ethereum Address: 0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9\n" 235 | ] 236 | } 237 | ], 238 | "source": [ 239 | "address = hex_hash[24:]\n", 240 | "print(\"Ethereum Address: 0x\" + address)" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": 24, 246 | "metadata": {}, 247 | "outputs": [ 248 | { 249 | "name": "stdout", 250 | "output_type": "stream", 251 | "text": [ 252 | "23a69c1653e4ebbb619b0b2cb8a9bad49892a8b9695d9a19d8f673ca991deae1\n" 253 | ] 254 | } 255 | ], 256 | "source": [ 257 | "# Let's calculate the EIP-55 mixed-capitalization checksum address\n", 258 | "# Take the lower-case address and hash it again, to produce a checksum\n", 259 | "\n", 260 | "address_hash_hex = encode_hex(keccak256(address))\n", 261 | "print(address_hash_hex)" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 25, 267 | "metadata": {}, 268 | "outputs": [ 269 | { 270 | "name": "stdout", 271 | "output_type": "stream", 272 | "text": [ 273 | "EIP-55 encoded Ethereum Address: 0x001d3F1ef827552Ae1114027BD3ECF1f086bA0F9\n" 274 | ] 275 | } 276 | ], 277 | "source": [ 278 | "# Simple implementation of EIP-55\n", 279 | "# For every alphabetic character of the address, \n", 280 | "# capitalize it if the corresponding character of the hash is greater than 8, \n", 281 | "\n", 282 | "a = \"\"\n", 283 | "for i, c in enumerate(address):\n", 284 | " if c in '0123456789':\n", 285 | " a = a + c \n", 286 | " elif int(address_hash_hex[i], 16) >= 8:\n", 287 | " a = a + c.upper()\n", 288 | " else:\n", 289 | " a = a + c.lower()\n", 290 | " \n", 291 | "print(\"EIP-55 encoded Ethereum Address: 0x\"+a)" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": null, 297 | "metadata": {}, 298 | "outputs": [], 299 | "source": [] 300 | } 301 | ], 302 | "metadata": { 303 | "kernelspec": { 304 | "display_name": "Python 3", 305 | "language": "python", 306 | "name": "python3" 307 | }, 308 | "language_info": { 309 | "codemirror_mode": { 310 | "name": "ipython", 311 | "version": 3 312 | }, 313 | "file_extension": ".py", 314 | "mimetype": "text/x-python", 315 | "name": "python", 316 | "nbconvert_exporter": "python", 317 | "pygments_lexer": "ipython3", 318 | "version": "3.5.2" 319 | } 320 | }, 321 | "nbformat": 4, 322 | "nbformat_minor": 2 323 | } 324 | -------------------------------------------------------------------------------- /code/misc/example_keys.txt: -------------------------------------------------------------------------------- 1 | $ echo -n "Mastering Ethereum" | keccak-256sum 2 | F8F8A2F43C8376CCB0871305060D7B27B0554D2CC72BCCF41B2705608452F315 - 3 | 4 | Private Key 5 | f8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315 6 | 7 | Public Key 8 | 046e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683bae07b83b5c38e5e2b0c8529d7fa3f64d46daa1ece2d9ac14cab9477d042c84c32ccd0 9 | 10 | Prefix: 04 11 | X: 12 | 6e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683bae07b 13 | Y: 14 | 83b5c38e5e2b0c8529d7fa3f64d46daa1ece2d9ac14cab9477d042c84c32ccd0 15 | 16 | ETH Address (mixed cap) 17 | 0x001d3F1ef827552Ae1114027BD3ECF1f086bA0F9 18 | 19 | ------- 20 | 21 | MetaMask seed 22 | piano tower spell inhale drama inside else quarter quality velvet small fit 23 | 24 | Available Accounts 25 | ================== 26 | (0) 0x9e713963a92c02317a681b9bb3065a8249de124f 27 | (1) 0xb0920c523d582040f2bcb1bd7fb1c7c1ecebdb34 28 | (2) 0xa49b21493176e73d5d24368cde4c1e6e5380e83a 29 | (3) 0xadb5191bc45ee6b6992a05a7cab778727700d469 30 | (4) 0x47121eacb0b0a61ff2699d702f7773b521ff8bea 31 | (5) 0x3a6e24b67bf25f5c1b988f068300e390ff398e0d 32 | (6) 0x64030e9b656b1fbdc568547dce3a8e7a6d75e4f6 33 | (7) 0x97656373117b650900d3732d8a3e7248afd10d63 34 | (8) 0x6e135145a1ea7cd97fb50cb2cc209766afde5f6b 35 | (9) 0xda9f13c519e5f34ebcae3d81c23660267861f7c0 36 | 37 | Private Keys 38 | ================== 39 | (0) 91c8360c4cb4b5fac45513a7213f31d4e4a7bfcb4630e9fbf074f42a203ac0b9 40 | (1) 7e06428bb2ad698b277cd77607505abb00faed5a99a9bf8064c40787e1e7b745 41 | (2) 3719fd869bc9d310b3334ee69530fdae03106adef47dd8c584a1d14a29b41d84 42 | (3) 6fb3a3070d2c0b32a8a181d3a33ca5cc3d4dd958b95fa0a28a1c6c4df698f919 43 | (4) 7cf6be18e131638a83675805294017a81d7b7a40271df17f83071845678eaa96 44 | (5) 5b758a7b39afc142c3293d17b5af401afd80835e5708654188545eda45d52d95 45 | (6) 32b5b6b830a5fa5cd6796bde303ce89ee645a6952b1d4251d048ea68dde8c702 46 | (7) 03e64537cef8909d54645c980c4d805be86930a095637fb7f7b22a9d2fb58e42 47 | (8) e6edb1ee507a967780f63d03e88135708d67d62a56611a9cff6144a596edceab 48 | (9) 0cc66101e2efe5bb72776925c878dfc2a934bb87787f3fe6f56ed6eecc70589e 49 | 50 | ------- 51 | 52 | Ganache Seed 53 | candy maple cake sugar pudding cream honey rich smooth crumble sweet treat 54 | 55 | Available Accounts 56 | ================== 57 | (0) 0x627306090abab3a6e1400e9345bc60c78a8bef57 58 | (1) 0xf17f52151ebef6c7334fad080c5704d77216b732 59 | (2) 0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef 60 | (3) 0x821aea9a577a9b44299b9c15c88cf3087f3b5544 61 | (4) 0x0d1d4e623d10f9fba5db95830f7d3839406c6af2 62 | (5) 0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e 63 | (6) 0x2191ef87e392377ec08e7c08eb105ef5448eced5 64 | (7) 0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5 65 | (8) 0x6330a553fc93768f612722bb8c2ec78ac90b3bbc 66 | (9) 0x5aeda56215b167893e80b4fe645ba6d5bab767de 67 | 68 | Private Keys 69 | ================== 70 | (0) c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3 71 | (1) ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f 72 | (2) 0dbbe8e4ae425a6d2687f1a7e3ba17bc98c673636790f1b8ad91193c05875ef1 73 | (3) c88b703fb08cbea894b6aeff5a544fb92e78a18e19814cd85da83b71f772aa6c 74 | (4) 388c684f0ba1ef5017716adb5d21a053ea8e90277d0868337519f97bede61418 75 | (5) 659cbb0e2411a44db63778987b1e22153c086a95eb6b18bdf89de078917abc63 76 | (6) 82d052c865f5763aad42add438569276c00d3d88a2d062d36b2bae914d58b8c8 77 | (7) aa3680d5d48a8283413f7a108367c7299ca73f553735860a87b08f39395618b7 78 | (8) 0f62d96d6675f32685bbdb8ac13cda7c23436f63efbb9d07700d8669ff12b7c4 79 | (9) 8d5366123cb560bb606379f90a0bfd4769eecc0557f1b362dcae9012b548b1e5 80 | 81 | ------- 82 | 83 | BIP39 example seed 84 | wolf juice proud gown wool unfair wall cliff insect more detail hub 85 | 86 | BIP32 root 87 | xprv9s21ZrQH143K2Q5FjzwmUXKS9adFPhHhKSGiWhvxoXBwBA5FzXEe3oF3RdzWKUEjuFH1MAHsvZpfBHmcZxHRLf6bR1GBzJLrmc5uGso6rNE 88 | 89 | Available Accounts 90 | ================== 91 | (0) 0x6adca879124a3e9a87b4b55a9ada0119e4468941 92 | (1) 0xe1544a42ae1e21de1dc2e8f689dd3ae5fd64d507 93 | (2) 0x9c1f74baf574a4fd56ac4d73dfbdb10559734a14 94 | (3) 0x1745ec0c503d3c88f0af096eb7e0407ef934f153 95 | (4) 0x11e27593a8ee5a98f9b2e752fabb159c7837d49f 96 | (5) 0xf5484b9e3928576915d275e82e6af1539f3a9597 97 | (6) 0x7e0da4dc380249ea4919910b3ca8dc9d023fe7be 98 | (7) 0x15fb650736bace2f76f527307cf6c1639642e066 99 | (8) 0xd5326ba2c5924912a5c773835ad521c925af0d00 100 | (9) 0xe518bf0ebf9da4ba0342ea5d3213cc9279002d8a 101 | 102 | Private Keys 103 | ================== 104 | (0) 9d147484b814e14c7c6d546325e493e962825045ba5f6407c199ffabf226b28d 105 | (1) ae343294259b84aaaa12ffa4b91a1dbea46d2760c5506e62e2e698582398752f 106 | (2) 55d226c0e62029b20a3e6239268d159a555da0097618102133b6bb9b9c4ef968 107 | (3) aa2fffcbf8c3cf4508c71925f8bf87d9e9b7240fe868a6ac4d846ef4e66e75cf 108 | (4) de61c4d9645f3ac9d9e93d1f05eee866179e4f4b0605bf52d4797f0df750d70f 109 | (5) f52301c634da761d51848448d6a22f49161e0f5f22cc83d393cf5a44698f2c44 110 | (6) a8005637219801f87ddd7e248c2e8d0c3013c0ebb450b35ee64caf55e221f900 111 | (7) b132c35e45e79bbd371b9d3a1a46f580667a64293b88d74831a59a9eac4bf4e0 112 | (8) a6b09fb86fd6682abb1cad43b8aa53897bf10696f8b6934f0b1e4fa11a69cfce 113 | (9) 8a0037949e186f8810dfeb5b38ee657fcf43d12bbdd58dacd7684de3a98c87a2 114 | -------------------------------------------------------------------------------- /code/misc/readme_wordcount.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import glob 3 | import re 4 | 5 | markup_files = glob.glob('*.asciidoc') 6 | 7 | wordcount = {} 8 | wordcount_sum = 0 9 | 10 | for markup_file in markup_files: 11 | markup_f = open(markup_file, 'r') 12 | markup_contents = markup_f.read() 13 | markup_f.close() 14 | wc = len(markup_contents.strip().split()) 15 | wordcount_sum += wc 16 | wordcount[markup_file] = wc 17 | print(wc, "\t", markup_file) 18 | print(wordcount_sum) 19 | 20 | readme_f = open('README.md','r') 21 | readme = readme_f.read() 22 | readme_f.close() 23 | 24 | wc_tag_re = re.compile("\| +(\[.*\])\((.*asciidoc)\) +\| +[\#]+ +\|(.*)$") 25 | 26 | readme_f = open('README.md','w') 27 | for line in readme.splitlines(): 28 | match_re = wc_tag_re.match(line) 29 | if match_re: 30 | wordcount_bar = "#" * ((wordcount[match_re.group(2)]//500) + 1) 31 | line = match_re.expand("| \g<1>(\g<2>) | " + wordcount_bar + " |\g<3>") 32 | readme_f.write(line+"\n") 33 | readme_f.close() 34 | -------------------------------------------------------------------------------- /code/truffle/CallExamples/contracts/CallExamples.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.22; 2 | 3 | contract calledContract { 4 | event callEvent(address sender, address origin, address from); 5 | function calledFunction() public { 6 | emit callEvent(msg.sender, tx.origin, this); 7 | } 8 | } 9 | 10 | library calledLibrary { 11 | event callEvent(address sender, address origin, address from); 12 | function calledFunction() public { 13 | emit callEvent(msg.sender, tx.origin, this); 14 | } 15 | } 16 | 17 | contract caller { 18 | 19 | function make_calls(calledContract _calledContract) public { 20 | 21 | // Calling the calledContract and calledLibrary directly 22 | _calledContract.calledFunction(); 23 | calledLibrary.calledFunction(); 24 | 25 | // Low level calls using the address object for calledContract 26 | require(address(_calledContract).call(bytes4(keccak256("calledFunction()")))); 27 | require(address(_calledContract).delegatecall(bytes4(keccak256("calledFunction()")))); 28 | 29 | 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /code/truffle/CallExamples/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/truffle/CallExamples/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /code/truffle/CallExamples/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | var caller = artifacts.require("caller"); 2 | var calledContract = artifacts.require("calledContract"); 3 | var calledLibrary = artifacts.require("calledLibrary"); 4 | 5 | module.exports = function(deployer) { 6 | 7 | deployer.deploy(calledContract); 8 | deployer.deploy(calledLibrary); 9 | 10 | deployer.link(calledLibrary, [caller]); 11 | 12 | deployer.deploy(caller); 13 | }; 14 | -------------------------------------------------------------------------------- /code/truffle/CallExamples/truffle-config.js: -------------------------------------------------------------------------------- 1 | // Install dependencies: 2 | // npm init 3 | // npm install --save-dev dotenv truffle-wallet-provider ethereumjs-wallet 4 | 5 | // Create .env in project root, with keys: 6 | // ROPSTEN_PRIVATE_KEY="123abc" 7 | // MAINNET_PRIVATE_KEY="123abc" 8 | 9 | require('dotenv').config(); 10 | const Web3 = require("web3"); 11 | const web3 = new Web3(); 12 | const WalletProvider = require("truffle-wallet-provider"); 13 | const Wallet = require('ethereumjs-wallet'); 14 | 15 | var mainNetPrivateKey = new Buffer(process.env["MAINNET_PRIVATE_KEY"], "hex") 16 | var mainNetWallet = Wallet.fromPrivateKey(mainNetPrivateKey); 17 | var mainNetProvider = new WalletProvider(mainNetWallet, "https://mainnet.infura.io/"); 18 | 19 | var ropstenPrivateKey = new Buffer(process.env["ROPSTEN_PRIVATE_KEY"], "hex") 20 | var ropstenWallet = Wallet.fromPrivateKey(ropstenPrivateKey); 21 | var ropstenProvider = new WalletProvider(ropstenWallet, "https://ropsten.infura.io/"); 22 | 23 | module.exports = { 24 | networks: { 25 | dev: { // Whatever network our local node connects to 26 | network_id: "*", // Match any network id 27 | host: "localhost", 28 | port: 8545, 29 | }, 30 | mainnet: { // Provided by Infura, load keys in .env file 31 | network_id: "1", 32 | provider: mainNetProvider, 33 | gas: 4600000, 34 | gasPrice: web3.toWei("20", "gwei"), 35 | }, 36 | ropsten: { // Provided by Infura, load keys in .env file 37 | network_id: "3", 38 | provider: ropstenProvider, 39 | gas: 4600000, 40 | gasPrice: web3.toWei("20", "gwei"), 41 | }, 42 | kovan: { 43 | network_id: 42, 44 | host: "localhost", // parity --chain=kovan 45 | port: 8545, 46 | gas: 5000000 47 | }, 48 | ganache: { // Ganache local test RPC blockchain 49 | network_id: "5777", 50 | host: "localhost", 51 | port: 7545, 52 | gas: 6721975, 53 | } 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /code/truffle/Faucet/contracts/Faucet.sol: -------------------------------------------------------------------------------- 1 | // Version of Solidity compiler this program was written for 2 | pragma solidity ^0.4.19; 3 | 4 | // Our first contract is a faucet! 5 | contract Faucet { 6 | 7 | // Give out ether to anyone who asks 8 | function withdraw(uint withdraw_amount) public { 9 | 10 | // Limit withdrawal amount 11 | require(withdraw_amount <= 100000000000000000); 12 | 13 | // Send the amount to the address that requested it 14 | msg.sender.transfer(withdraw_amount); 15 | } 16 | 17 | // Accept any incoming amount 18 | function () public payable {} 19 | 20 | } 21 | -------------------------------------------------------------------------------- /code/truffle/Faucet/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/truffle/Faucet/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /code/truffle/Faucet/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | var Faucet = artifacts.require("./Faucet.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Faucet); 5 | }; 6 | -------------------------------------------------------------------------------- /code/truffle/Faucet/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | localnode: { // Whatever network our local node connects to 4 | network_id: "*", // Match any network id 5 | host: "localhost", 6 | port: 8545, 7 | gas: 4700000, 8 | gasPrice: 100000000000, 9 | } 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /code/truffle/FaucetEvents/contracts/Faucet.sol: -------------------------------------------------------------------------------- 1 | // Version of Solidity compiler this program was written for 2 | pragma solidity ^0.4.22; 3 | 4 | contract owned { 5 | address owner; 6 | // Contract constructor: set owner 7 | constructor() { 8 | owner = msg.sender; 9 | } 10 | // Access control modifier 11 | modifier onlyOwner { 12 | require(msg.sender == owner, "Only the contract owner can call this function"); 13 | _; 14 | } 15 | } 16 | 17 | contract mortal is owned { 18 | // Contract destructor 19 | function destroy() public onlyOwner { 20 | selfdestruct(owner); 21 | } 22 | } 23 | 24 | contract Faucet is mortal { 25 | event Withdrawal(address indexed to, uint amount); 26 | event Deposit(address indexed from, uint amount); 27 | 28 | // Give out ether to anyone who asks 29 | function withdraw(uint withdraw_amount) public { 30 | // Limit withdrawal amount 31 | require(withdraw_amount <= 0.1 ether); 32 | require(this.balance >= withdraw_amount, 33 | "Insufficient balance in faucet for withdrawal request"); 34 | // Send the amount to the address that requested it 35 | msg.sender.transfer(withdraw_amount); 36 | emit Withdrawal(msg.sender, withdraw_amount); 37 | } 38 | // Accept any incoming amount 39 | function () public payable { 40 | emit Deposit(msg.sender, msg.value); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /code/truffle/FaucetEvents/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/truffle/FaucetEvents/gas_estimates.js: -------------------------------------------------------------------------------- 1 | var FaucetContract = artifacts.require("Faucet.sol"); 2 | 3 | FaucetContract.web3.eth.getGasPrice(function(error, result) { 4 | var gasPrice = Number(result); 5 | console.log("Gas Price is " + gasPrice + " wei"); // "10000000000000" 6 | 7 | // Get the contract instance 8 | FaucetContract.deployed().then(function(FaucetContractInstance) { 9 | 10 | // Use the keyword 'estimateGas' after the function name to get the gas estimation for this particular function (aprove) 11 | FaucetContractInstance.send(web3.toWei(1, "ether")); 12 | return FaucetContractInstance.withdraw.estimateGas(web3.toWei(0.1, "ether")); 13 | 14 | }).then(function(result) { 15 | var gas = Number(result); 16 | 17 | console.log("gas estimation = " + gas + " units"); 18 | console.log("gas cost estimation = " + (gas * gasPrice) + " wei"); 19 | console.log("gas cost estimation = " + FaucetContract.web3.fromWei((gas * gasPrice), 'ether') + " ether"); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /code/truffle/FaucetEvents/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /code/truffle/FaucetEvents/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | var Faucet = artifacts.require("./Faucet.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Faucet); 5 | }; 6 | -------------------------------------------------------------------------------- /code/truffle/FaucetEvents/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | localnode: { // Whatever network our local node connects to 4 | network_id: "*", // Match any network id 5 | host: "localhost", 6 | port: 8545, 7 | gas: 4700000, 8 | gasPrice: 100000000000, 9 | } 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /code/truffle/METoken/contracts/METoken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import 'zeppelin-solidity/contracts/token/ERC20/StandardToken.sol'; 4 | 5 | contract METoken is StandardToken { 6 | string public constant name = 'Mastering Ethereum Token'; 7 | string public constant symbol = 'MET'; 8 | uint8 public constant decimals = 2; 9 | uint constant _initial_supply = 2100000000; 10 | 11 | function METoken() public { 12 | totalSupply_ = _initial_supply; 13 | balances[msg.sender] = _initial_supply; 14 | Transfer(address(0), msg.sender, _initial_supply); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /code/truffle/METoken/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/truffle/METoken/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function(deployer) { 4 | // Deploy the Migrations contract as our only task 5 | deployer.deploy(Migrations); 6 | }; 7 | -------------------------------------------------------------------------------- /code/truffle/METoken/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | var METoken = artifacts.require("METoken"); 2 | 3 | module.exports = function(deployer) { 4 | // Deploy the METoken contract as our only task 5 | deployer.deploy(METoken); 6 | }; 7 | -------------------------------------------------------------------------------- /code/truffle/METoken/test/METoken.test.js: -------------------------------------------------------------------------------- 1 | const METoken = artifacts.require("METoken"); 2 | 3 | contract('METoken', accounts => { 4 | 5 | beforeEach(async function () { 6 | InstanceMEToken = await METoken.new(); 7 | }); 8 | 9 | it('has a name', async function () { 10 | const name = await InstanceMEToken.name(); 11 | assert.notEqual(name, "", "token name shouldn't be empty"); 12 | }); 13 | 14 | it('has a symbol', async function () { 15 | const symbol = await InstanceMEToken.symbol(); 16 | assert.notEqual(symbol, "", "token symbol shouldn't be empty"); 17 | }); 18 | 19 | it('has decimals', async function () { 20 | const decimals = await InstanceMEToken.decimals(); 21 | assert.notEqual(decimals, "", "token decimals shouldn't be empty"); 22 | }); 23 | 24 | it('owner has 2100000000 tokens', async function () { 25 | const balance = await InstanceMEToken.balanceOf(accounts[0]); 26 | assert.equal(balance, 2100000000, "owner doesn't have 2100000000 tokens"); 27 | }); 28 | }); -------------------------------------------------------------------------------- /code/truffle/METoken/truffle-config.js: -------------------------------------------------------------------------------- 1 | // Install dependencies: 2 | // npm init 3 | // npm install --save-dev dotenv truffle-wallet-provider ethereumjs-wallet 4 | 5 | // Create .env in project root, with keys: 6 | // ROPSTEN_PRIVATE_KEY="123abc" 7 | // MAINNET_PRIVATE_KEY="123abc" 8 | 9 | require('dotenv').config(); 10 | const Web3 = require("web3"); 11 | const web3 = new Web3(); 12 | const WalletProvider = require("truffle-wallet-provider"); 13 | const Wallet = require('ethereumjs-wallet'); 14 | 15 | var mainNetPrivateKey = new Buffer(process.env["MAINNET_PRIVATE_KEY"], "hex") 16 | var mainNetWallet = Wallet.fromPrivateKey(mainNetPrivateKey); 17 | var mainNetProvider = new WalletProvider(mainNetWallet, "https://mainnet.infura.io/"); 18 | 19 | var ropstenPrivateKey = new Buffer(process.env["ROPSTEN_PRIVATE_KEY"], "hex") 20 | var ropstenWallet = Wallet.fromPrivateKey(ropstenPrivateKey); 21 | var ropstenProvider = new WalletProvider(ropstenWallet, "https://ropsten.infura.io/"); 22 | 23 | module.exports = { 24 | networks: { 25 | dev: { // Whatever network our local node connects to 26 | network_id: "*", // Match any network id 27 | host: "localhost", 28 | port: 8545, 29 | }, 30 | mainnet: { // Provided by Infura, load keys in .env file 31 | network_id: "1", 32 | provider: mainNetProvider, 33 | gas: 4600000, 34 | gasPrice: web3.toWei("20", "gwei"), 35 | }, 36 | ropsten: { // Provided by Infura, load keys in .env file 37 | network_id: "3", 38 | provider: ropstenProvider, 39 | gas: 4600000, 40 | gasPrice: web3.toWei("20", "gwei"), 41 | }, 42 | kovan: { 43 | network_id: 42, 44 | host: "localhost", // parity --chain=kovan 45 | port: 8545, 46 | gas: 5000000 47 | }, 48 | ganache: { // Ganache local test RPC blockchain 49 | network_id: "5777", 50 | host: "localhost", 51 | port: 7545, 52 | gas: 6721975, 53 | } 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /code/truffle/METoken_Faucet/contracts/Faucet.sol: -------------------------------------------------------------------------------- 1 | // Version of Solidity compiler this program was written for 2 | pragma solidity ^0.4.19; 3 | 4 | // Our first contract is a faucet! 5 | contract Faucet { 6 | 7 | // Give out ether to anyone who asks 8 | function withdraw(uint withdraw_amount) public { 9 | 10 | // Limit withdrawal amount 11 | require(withdraw_amount <= 100000000000000000); 12 | 13 | // Send the amount to the address that requested it 14 | msg.sender.transfer(withdraw_amount); 15 | } 16 | 17 | // Accept any incoming amount 18 | function () public payable {} 19 | 20 | } 21 | -------------------------------------------------------------------------------- /code/truffle/METoken_Faucet/contracts/METoken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import 'zeppelin-solidity/contracts/token/ERC20/StandardToken.sol'; 4 | 5 | contract METoken is StandardToken { 6 | string public constant name = 'Mastering Ethereum Token'; 7 | string public constant symbol = 'MET'; 8 | uint8 public constant decimals = 2; 9 | uint constant _initial_supply = 2100000000; 10 | 11 | function METoken() public { 12 | totalSupply_ = _initial_supply; 13 | balances[msg.sender] = _initial_supply; 14 | Transfer(address(0), msg.sender, _initial_supply); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /code/truffle/METoken_Faucet/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/truffle/METoken_Faucet/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function(deployer) { 4 | // Deploy the Migrations contract as our only task 5 | deployer.deploy(Migrations); 6 | }; 7 | -------------------------------------------------------------------------------- /code/truffle/METoken_Faucet/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | var METoken = artifacts.require("METoken"); 2 | 3 | module.exports = function(deployer) { 4 | // Deploy the METoken contract as our only task 5 | deployer.deploy(METoken); 6 | }; 7 | -------------------------------------------------------------------------------- /code/truffle/METoken_Faucet/migrations/3_deploy_faucet.js: -------------------------------------------------------------------------------- 1 | var Faucet = artifacts.require("Faucet"); 2 | 3 | module.exports = function(deployer) { 4 | // Deploy the Faucet contract as our only task 5 | deployer.deploy(Faucet); 6 | }; 7 | -------------------------------------------------------------------------------- /code/truffle/METoken_Faucet/truffle-config.js: -------------------------------------------------------------------------------- 1 | // Install dependencies: 2 | // npm init 3 | // npm install --save-dev dotenv truffle-wallet-provider ethereumjs-wallet 4 | 5 | // Create .env in project root, with keys: 6 | // ROPSTEN_PRIVATE_KEY="123abc" 7 | // MAINNET_PRIVATE_KEY="123abc" 8 | 9 | require('dotenv').config(); 10 | const Web3 = require("web3"); 11 | const web3 = new Web3(); 12 | const WalletProvider = require("truffle-wallet-provider"); 13 | const Wallet = require('ethereumjs-wallet'); 14 | 15 | var mainNetPrivateKey = new Buffer(process.env["MAINNET_PRIVATE_KEY"], "hex") 16 | var mainNetWallet = Wallet.fromPrivateKey(mainNetPrivateKey); 17 | var mainNetProvider = new WalletProvider(mainNetWallet, "https://mainnet.infura.io/"); 18 | 19 | var ropstenPrivateKey = new Buffer(process.env["ROPSTEN_PRIVATE_KEY"], "hex") 20 | var ropstenWallet = Wallet.fromPrivateKey(ropstenPrivateKey); 21 | var ropstenProvider = new WalletProvider(ropstenWallet, "https://ropsten.infura.io/"); 22 | 23 | module.exports = { 24 | networks: { 25 | dev: { // Whatever network our local node connects to 26 | network_id: "*", // Match any network id 27 | host: "localhost", 28 | port: 8545, 29 | }, 30 | mainnet: { // Provided by Infura, load keys in .env file 31 | network_id: "1", 32 | provider: mainNetProvider, 33 | gas: 4600000, 34 | gasPrice: web3.toWei("20", "gwei"), 35 | }, 36 | ropsten: { // Provided by Infura, load keys in .env file 37 | network_id: "3", 38 | provider: ropstenProvider, 39 | gas: 4600000, 40 | gasPrice: web3.toWei("20", "gwei"), 41 | }, 42 | kovan: { 43 | network_id: 42, 44 | host: "localhost", // parity --chain=kovan 45 | port: 8545, 46 | gas: 5000000 47 | }, 48 | ganache: { // Ganache local test RPC blockchain 49 | network_id: "5777", 50 | host: "localhost", 51 | port: 7545, 52 | gas: 6721975, 53 | } 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /code/truffle/METoken_METFaucet/contracts/METFaucet.sol: -------------------------------------------------------------------------------- 1 | // Version of Solidity compiler this program was written for 2 | pragma solidity ^0.4.19; 3 | 4 | import 'zeppelin-solidity/contracts/token/ERC20/StandardToken.sol'; 5 | 6 | 7 | // A faucet for ERC20 token MET 8 | contract METFaucet { 9 | 10 | StandardToken public METoken; 11 | address public METOwner; 12 | 13 | // METFaucet constructor, provide the address of METoken contract and 14 | // the owner address we will be approved to transferFrom 15 | function METFaucet(address _METoken, address _METOwner) public { 16 | 17 | // Initialize the METoken from the address provided 18 | METoken = StandardToken(_METoken); 19 | METOwner = _METOwner; 20 | } 21 | 22 | function withdraw(uint withdraw_amount) public { 23 | 24 | // Limit withdrawal amount to 10 MET 25 | require(withdraw_amount <= 1000); 26 | 27 | // Use the transferFrom function of METoken 28 | METoken.transferFrom(METOwner, msg.sender, withdraw_amount); 29 | } 30 | 31 | // REJECT any incoming ether 32 | function () public payable { revert(); } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /code/truffle/METoken_METFaucet/contracts/METoken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import 'zeppelin-solidity/contracts/token/ERC20/StandardToken.sol'; 4 | 5 | contract METoken is StandardToken { 6 | string public constant name = 'Mastering Ethereum Token'; 7 | string public constant symbol = 'MET'; 8 | uint8 public constant decimals = 2; 9 | uint constant _initial_supply = 2100000000; 10 | 11 | function METoken() public { 12 | totalSupply_ = _initial_supply; 13 | balances[msg.sender] = _initial_supply; 14 | Transfer(address(0), msg.sender, _initial_supply); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /code/truffle/METoken_METFaucet/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/truffle/METoken_METFaucet/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function(deployer) { 4 | // Deploy the Migrations contract as our only task 5 | deployer.deploy(Migrations); 6 | }; 7 | -------------------------------------------------------------------------------- /code/truffle/METoken_METFaucet/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | var METoken = artifacts.require("METoken"); 2 | var METFaucet = artifacts.require("METFaucet"); 3 | var owner = web3.eth.accounts[0]; 4 | 5 | module.exports = function(deployer) { 6 | 7 | // Deploy the METoken contract first 8 | deployer.deploy(METoken, {from: owner}).then(function() { 9 | // then deploy METFaucet and pass the address of METoken 10 | // and the address of the owner of all the MET who will approve METFaucet 11 | return deployer.deploy(METFaucet, METoken.address, owner); 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /code/truffle/METoken_METFaucet/test/test_approve_transferFrom.js: -------------------------------------------------------------------------------- 1 | var METoken = artifacts.require("METoken"); 2 | var METFaucet = artifacts.require("METFaucet"); 3 | 4 | contract('METoken', function(accounts) { 5 | var Alice = accounts[0]; 6 | var Bob = accounts[1]; 7 | 8 | 9 | it("should approve METFaucet for up to 1000 MET ", function() { 10 | return METoken.deployed().then(instance => { 11 | instance.approve(METFaucet.address, 100000).then(function(result) { 12 | assert(result.logs[0].event, "Approval", "Expected approval event") 13 | }); 14 | }); 15 | }); 16 | it("should allow Bob to withdraw 10 MET from METFaucet", function() { 17 | return METFaucet.deployed().then(instance => { 18 | instance.withdraw(1000, {from: Bob}).then(function(result) { 19 | console.log(result.logs); 20 | }); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /code/truffle/METoken_METFaucet/truffle-config.js: -------------------------------------------------------------------------------- 1 | // Install dependencies: 2 | // npm init 3 | // npm install --save-dev dotenv truffle-wallet-provider ethereumjs-wallet 4 | 5 | // Create .env in project root, with keys: 6 | // ROPSTEN_PRIVATE_KEY="123abc" 7 | // MAINNET_PRIVATE_KEY="123abc" 8 | 9 | require('dotenv').config(); 10 | const Web3 = require("web3"); 11 | const web3 = new Web3(); 12 | const WalletProvider = require("truffle-wallet-provider"); 13 | const Wallet = require('ethereumjs-wallet'); 14 | 15 | var mainNetPrivateKey = new Buffer(process.env["MAINNET_PRIVATE_KEY"], "hex") 16 | var mainNetWallet = Wallet.fromPrivateKey(mainNetPrivateKey); 17 | var mainNetProvider = new WalletProvider(mainNetWallet, "https://mainnet.infura.io/"); 18 | 19 | var ropstenPrivateKey = new Buffer(process.env["ROPSTEN_PRIVATE_KEY"], "hex") 20 | var ropstenWallet = Wallet.fromPrivateKey(ropstenPrivateKey); 21 | var ropstenProvider = new WalletProvider(ropstenWallet, "https://ropsten.infura.io/"); 22 | 23 | module.exports = { 24 | networks: { 25 | dev: { // Whatever network our local node connects to 26 | network_id: "*", // Match any network id 27 | host: "localhost", 28 | port: 8545, 29 | }, 30 | mainnet: { // Provided by Infura, load keys in .env file 31 | network_id: "1", 32 | provider: mainNetProvider, 33 | gas: 4600000, 34 | gasPrice: web3.toWei("20", "gwei"), 35 | }, 36 | ropsten: { // Provided by Infura, load keys in .env file 37 | network_id: "3", 38 | provider: ropstenProvider, 39 | gas: 4600000, 40 | gasPrice: web3.toWei("20", "gwei"), 41 | }, 42 | kovan: { 43 | network_id: 42, 44 | host: "localhost", // parity --chain=kovan 45 | port: 8545, 46 | gas: 5000000 47 | }, 48 | ganache: { // Ganache local test RPC blockchain 49 | network_id: "5777", 50 | host: "localhost", 51 | port: 7545, 52 | gas: 6721975, 53 | } 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /code/truffle/console/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/truffle/console/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /code/truffle/console/nonce.js: -------------------------------------------------------------------------------- 1 | console.log("getTransactionCount (confirmed)"); 2 | console.log(web3.eth.getTransactionCount("0x9e713963a92c02317a681b9bb3065a8249de124f")); 3 | 4 | console.log("getTransactionCount (pending)"); 5 | console.log(web3.eth.getTransactionCount("0x9e713963a92c02317a681b9bb3065a8249de124f", "pending")); 6 | 7 | console.log("sendTransaction"); 8 | console.log(web3.eth.sendTransaction({from: web3.eth.accounts[0], to: "0xB0920c523d582040f2BCB1bD7FB1c7C1ECEbdB34", value: web3.toWei(0.01, "ether")})); 9 | 10 | console.log("getTransactionCount (confirmed)"); 11 | console.log(web3.eth.getTransactionCount("0x9e713963a92c02317a681b9bb3065a8249de124f")); 12 | 13 | console.log("getTransactionCount (pending)"); 14 | console.log(web3.eth.getTransactionCount("0x9e713963a92c02317a681b9bb3065a8249de124f", "pending")); 15 | 16 | console.log("sendTransaction"); 17 | console.log(web3.eth.sendTransaction({from: web3.eth.accounts[0], to: "0xB0920c523d582040f2BCB1bD7FB1c7C1ECEbdB34", value: web3.toWei(0.01, "ether")})); 18 | 19 | console.log("getTransactionCount (confirmed)"); 20 | console.log(web3.eth.getTransactionCount("0x9e713963a92c02317a681b9bb3065a8249de124f")); 21 | 22 | console.log("getTransactionCount (pending)"); 23 | console.log(web3.eth.getTransactionCount("0x9e713963a92c02317a681b9bb3065a8249de124f", "pending")); 24 | 25 | console.log("sendTransaction"); 26 | console.log(web3.eth.sendTransaction({from: web3.eth.accounts[0], to: "0xB0920c523d582040f2BCB1bD7FB1c7C1ECEbdB34", value: web3.toWei(0.01, "ether")})); 27 | 28 | console.log("getTransactionCount (confirmed)"); 29 | console.log(web3.eth.getTransactionCount("0x9e713963a92c02317a681b9bb3065a8249de124f")); 30 | 31 | console.log("getTransactionCount (pending)"); 32 | console.log(web3.eth.getTransactionCount("0x9e713963a92c02317a681b9bb3065a8249de124f", "pending")); 33 | 34 | console.log("sendTransaction"); 35 | console.log(web3.eth.sendTransaction({from: web3.eth.accounts[0], to: "0xB0920c523d582040f2BCB1bD7FB1c7C1ECEbdB34", value: web3.toWei(0.01, "ether")})); 36 | -------------------------------------------------------------------------------- /code/truffle/console/truffle-config.js: -------------------------------------------------------------------------------- 1 | // Install dependencies: 2 | // npm init 3 | // npm install --save-dev dotenv truffle-wallet-provider ethereumjs-wallet 4 | 5 | // Create .env in project root, with keys: 6 | // ROPSTEN_PRIVATE_KEY="123abc" 7 | // MAINNET_PRIVATE_KEY="123abc" 8 | 9 | require('dotenv').config(); 10 | const Web3 = require("web3"); 11 | const web3 = new Web3(); 12 | const WalletProvider = require("truffle-wallet-provider"); 13 | const Wallet = require('ethereumjs-wallet'); 14 | 15 | var mainNetPrivateKey = new Buffer(process.env["MAINNET_PRIVATE_KEY"], "hex") 16 | var mainNetWallet = Wallet.fromPrivateKey(mainNetPrivateKey); 17 | var mainNetProvider = new WalletProvider(mainNetWallet, "https://mainnet.infura.io/"); 18 | 19 | var ropstenPrivateKey = new Buffer(process.env["ROPSTEN_PRIVATE_KEY"], "hex") 20 | var ropstenWallet = Wallet.fromPrivateKey(ropstenPrivateKey); 21 | var ropstenProvider = new WalletProvider(ropstenWallet, "https://ropsten.infura.io/"); 22 | 23 | module.exports = { 24 | networks: { 25 | dev: { // Whatever network our local node connects to 26 | network_id: "*", // Match any network id 27 | host: "localhost", 28 | port: 8545, 29 | }, 30 | mainnet: { // Provided by Infura, load keys in .env file 31 | network_id: "1", 32 | provider: mainNetProvider, 33 | gas: 4600000, 34 | gasPrice: web3.toWei("20", "gwei"), 35 | }, 36 | ropsten: { // Provided by Infura, load keys in .env file 37 | network_id: "3", 38 | provider: ropstenProvider, 39 | gas: 4600000, 40 | gasPrice: web3.toWei("20", "gwei"), 41 | }, 42 | kovan: { 43 | network_id: 42, 44 | host: "localhost", // parity --chain=kovan 45 | port: 8545, 46 | gas: 5000000 47 | }, 48 | ganache: { // Ganache local test RPC blockchain 49 | network_id: "5777", 50 | host: "localhost", 51 | port: 7545, 52 | gas: 6721975, 53 | } 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /code/web3js/raw_tx/raw_tx_demo.js: -------------------------------------------------------------------------------- 1 | // Load requirements first: 2 | // 3 | // npm init 4 | // npm install ethereumjs-tx 5 | // 6 | // Run with: $ node raw_tx_demo.js 7 | const ethTx = require('ethereumjs-tx'); 8 | 9 | const txData = { 10 | nonce: '0x0', 11 | gasPrice: '0x09184e72a000', 12 | gasLimit: '0x30000', 13 | to: '0xb0920c523d582040f2bcb1bd7fb1c7c1ecebdb34', 14 | value: '0x00', 15 | data: '', 16 | v: "0x1c", // Ethereum main net chainID 17 | r: 0, 18 | s: 0 19 | }; 20 | 21 | tx = new ethTx(txData); 22 | console.log('RLP-Encoded Tx: 0x' + tx.serialize().toString('hex')) 23 | 24 | txHash = tx.hash(); // This step encodes into RLP and calculates the hash 25 | console.log('Tx Hash: 0x' + txHash.toString('hex')) 26 | 27 | // Sign transaction 28 | const privKey = Buffer.from('91c8360c4cb4b5fac45513a7213f31d4e4a7bfcb4630e9fbf074f42a203ac0b9', 'hex'); 29 | tx.sign(privKey); 30 | 31 | serializedTx = tx.serialize(); 32 | rawTx = 'Signed Raw Transaction: 0x' + serializedTx.toString('hex'); 33 | console.log(rawTx) 34 | -------------------------------------------------------------------------------- /code/web3js/web3js_demo/web3-contract-basic-interaction-async-await.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * @author Francisco Javier Rojas García 5 | */ 6 | 7 | console.log('Mastering Ethereum - web3.js basic interactions using async/await') 8 | console.log('Author: Francisco Javier Rojas García - fjrojasgarcia@gmail.com') 9 | 10 | var Web3 = require('web3'); 11 | var fs = require('fs') 12 | 13 | // Prepare your Infura host url 14 | var infura_host = "https://kovan.infura.io" 15 | 16 | // Instantiate web3 provider 17 | var web3 = new Web3(infura_host); 18 | 19 | // Let's do some basic interactions at web3 level 20 | async function basicInterations() { 21 | // Let's see the Protocol Version 22 | var protocolVersion = await web3.eth.getProtocolVersion(); 23 | console.log(`Protocol Version: ${protocolVersion}`); 24 | 25 | // Now I'm curious about the current gas price 26 | var gasPrice = await web3.eth.getGasPrice(); 27 | console.log(`Gas Price: ${gasPrice}`); 28 | 29 | // And, Whats the last mined block in my chain? 30 | var blockNumber = await web3.eth.getBlockNumber(); 31 | console.log(`Block Number: ${blockNumber}`); 32 | 33 | // Now let's dive into some basics actions with a contract 34 | // We will use the contract at; 35 | // https://kovan.etherscan.io/address/0xd0a1e359811322d97991e03f863a0c30c2cf029c#code 36 | 37 | // First things first, let's initialize our contract address 38 | var our_contract_address = "0xd0A1E359811322d97991E03f863a0C30C2cF029C"; 39 | 40 | // Let's see its balance 41 | var balance = await web3.eth.getBalance(our_contract_address); 42 | console.log(`Balance of ${our_contract_address}: ${balance}`); 43 | 44 | // Now let's see its byte code 45 | var code = await web3.eth.getCode(our_contract_address); 46 | console.log("Contract code: ----------------------------------------------\n"); 47 | console.log(code); 48 | console.log("-------------------------------------------------------------\n"); 49 | 50 | // Let's initialize our contract url in Etherescan for Kovan chain 51 | var etherescan_url = `http://kovan.etherscan.io/api?module=contract&action=getabi&address=${our_contract_address}` 52 | console.log(etherescan_url); 53 | 54 | var client = require('node-rest-client-promise').Client(); 55 | 56 | var etherescan_response = await client.getPromise(etherescan_url) 57 | 58 | // Leave this two lines for future object analysis 59 | //const util = require('util') 60 | //console.log(util.inspect(etherescan_response, false, null)) 61 | 62 | // We get here our contract ABI 63 | our_contract_abi = JSON.parse(etherescan_response.data.result); 64 | 65 | // Let's instantiate our contract object 66 | var our_contract = await new web3.eth.Contract(our_contract_abi, our_contract_address); 67 | 68 | // Let's see our contract address 69 | console.log(`Our Contract address: ${our_contract._address}`); 70 | 71 | // or in this other way 72 | console.log(`Our Contract address in other way: ${our_contract.options.address}`); 73 | 74 | // Now our contract abi 75 | console.log("Our contract abi: " + JSON.stringify(our_contract.options.jsonInterface)); 76 | 77 | // This is turning more interesting, let's see what's going with our contract methods 78 | // Now let's see our contract total supply 79 | var totalSupply = await our_contract.methods.totalSupply().call(); 80 | console.log(`Total Supply of Our Contract address ${our_contract._address}: ${totalSupply}`); 81 | 82 | // Now let's see our contract public variable name 83 | var name = await our_contract.methods.name().call(); 84 | console.log(`Public variable name of our Contract address ${our_contract._address}: ${name}`); 85 | 86 | // Now let's see our contract public variable symbol 87 | var symbol = await our_contract.methods.symbol().call(); 88 | console.log(`Public variable symbol of our Contract address ${our_contract._address}: ${symbol}`); 89 | } 90 | 91 | // Let's interact with a node 92 | basicInterations(); 93 | -------------------------------------------------------------------------------- /code/web3js/web3js_demo/web3-contract-basic-interaction.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * @author Francisco Javier Rojas García 5 | */ 6 | 7 | // Take a closer look at the web3 1.0 documentation for calling methods (it's very different from the 0.2x API). 8 | // https://stackoverflow.com/questions/48547268/smart-contract-method-is-not-a-function-in-web3 9 | 10 | console.log('Mastering Ethereum - web3.js basic interactions') 11 | console.log('Author: Francisco Javier Rojas García - fjrojasgarcia@gmail.com') 12 | 13 | const optionDefinitions = [ 14 | { name: 'localRPC', alias: 'l', type: Boolean }, 15 | { name: 'infuraFileToken', type: String, defaultOption: true } 16 | ] 17 | 18 | const commandLineArgs = require('command-line-args') 19 | const options = commandLineArgs(optionDefinitions) 20 | 21 | var Web3 = require('web3'); 22 | var fs = require('fs') 23 | 24 | if (options.infuraFileToken && !options.localRPC) { 25 | console.log(options.infuraFileToken); 26 | 27 | // Loading an Infura Token from a file 28 | var infura_token = fs.readFileSync(options.infuraFileToken, 'utf8'); 29 | 30 | // Show your Infura token 31 | console.log(infura_token); 32 | 33 | // Prepare your Infura host url 34 | var infura_host = `https://kovan.infura.io/${infura_token}` 35 | 36 | } else { 37 | console.log('Not argument found for infura token'); 38 | 39 | // Prepare your Infura host url 40 | var infura_host = "https://kovan.infura.io" 41 | 42 | } 43 | 44 | // Show your Infura host url for your web3 connection 45 | console.log(infura_host); 46 | 47 | // Instantiate web3 provider 48 | var web3 = new Web3(infura_host); 49 | 50 | // Let's do some basic interactions at web3 level 51 | // Let's see the Protocol Version 52 | web3.eth.getProtocolVersion().then(function(protocolVersion) { 53 | console.log(`Protocol Version: ${protocolVersion}`); 54 | }) 55 | 56 | // Now I'm curious about the current gas price 57 | web3.eth.getGasPrice().then(function(gasPrice) { 58 | console.log(`Gas Price: ${gasPrice}`); 59 | }) 60 | 61 | // And, Whats the last mined block in my chain? 62 | web3.eth.getBlockNumber().then(function(blockNumber) { 63 | console.log(`Block Number: ${blockNumber}`); 64 | }) 65 | 66 | // Now let's dive into some basics actions with a contract 67 | // We will use the contract at; 68 | // https://kovan.etherscan.io/address/0xd0a1e359811322d97991e03f863a0c30c2cf029c#code 69 | 70 | // First things first, let's initialize our contract address 71 | var our_contract_address = "0xd0A1E359811322d97991E03f863a0C30C2cF029C"; 72 | 73 | // Let's see its balance 74 | web3.eth.getBalance(our_contract_address).then(function(balance) { 75 | console.log(`Balance of ${our_contract_address}: ${balance}`); 76 | }) 77 | 78 | // Now let's see its byte code 79 | web3.eth.getCode(our_contract_address).then(function(code) { 80 | console.log("Contract code: ----------------------------------------------\n"); 81 | console.log(code); 82 | console.log("-------------------------------------------------------------\n"); 83 | }) 84 | 85 | // Let's initialize our contract url in Etherescan for Kovan chain 86 | var etherescan_url = `http://kovan.etherscan.io/api?module=contract&action=getabi&address=${our_contract_address}` 87 | console.log(etherescan_url); 88 | 89 | var client = require('node-rest-client-promise').Client(); 90 | 91 | // Now we are going to deal with the contract from web3.js in a non-block fashion (async mode) 92 | client.getPromise(etherescan_url) 93 | .then((client_promise) => { 94 | // Leave this two lines for fure object analisys 95 | //const util = require('util') 96 | //console.log(util.inspect(client_promise, false, null)) 97 | 98 | // We get here our contract ABI 99 | our_contract_abi = JSON.parse(client_promise.data.result); 100 | 101 | // And now we create a promise to consume later 102 | return new Promise((resolve, reject) => { 103 | var our_contract = new web3.eth.Contract(our_contract_abi, our_contract_address); 104 | try { 105 | // If all goes well 106 | resolve(our_contract); 107 | } catch (ex) { 108 | // If something goes wrong 109 | reject(ex); 110 | } 111 | }); 112 | 113 | }) 114 | .then((our_contract) => { 115 | // Let's see our contract address 116 | console.log(`Our Contract address: ${our_contract._address}`); 117 | 118 | // or in this other way 119 | console.log(`Our Contract address in other way: ${our_contract.options.address}`); 120 | 121 | // Now our contract abi 122 | console.log("Our contract abi: " + JSON.stringify(our_contract.options.jsonInterface)); 123 | 124 | // This is turning more interesting, let's see what's going with our contract methods 125 | // Now let's see our contract total supply in a callback fashion 126 | our_contract.methods.totalSupply().call(function(err, totalSupply) { 127 | if (!err) { 128 | console.log(`Total Supply with a callback: ${totalSupply}`); 129 | } else { 130 | console.log(err); 131 | } 132 | }); 133 | 134 | // Or you can use the returned Promise instead of passing in the callback: 135 | our_contract.methods.totalSupply().call().then(function(totalSupply){ 136 | console.log(`Total Supply with a promise: ${totalSupply}`); 137 | }).catch(function(err) { 138 | console.log(err); 139 | }); 140 | 141 | }) 142 | -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/cover.jpg -------------------------------------------------------------------------------- /images/Faucet_disassembled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/Faucet_disassembled.png -------------------------------------------------------------------------------- /images/Faucet_jumpi_instruction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/Faucet_jumpi_instruction.png -------------------------------------------------------------------------------- /images/TransferToFriend.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/TransferToFriend.gif -------------------------------------------------------------------------------- /images/approve_transferFrom_workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/approve_transferFrom_workflow.png -------------------------------------------------------------------------------- /images/architecture_diagram_webapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/architecture_diagram_webapp.png -------------------------------------------------------------------------------- /images/aws-topology-simple-bootnode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/aws-topology-simple-bootnode.png -------------------------------------------------------------------------------- /images/bip39-part1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/bip39-part1.png -------------------------------------------------------------------------------- /images/bip39-part2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/bip39-part2.png -------------------------------------------------------------------------------- /images/bip39_web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/bip39_web.png -------------------------------------------------------------------------------- /images/block_explorer_account_history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/block_explorer_account_history.png -------------------------------------------------------------------------------- /images/branches.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/branches.png -------------------------------------------------------------------------------- /images/chat-on-gitter.svg: -------------------------------------------------------------------------------- 1 | chatchaton gitteron gitter -------------------------------------------------------------------------------- /images/contract_published.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/contract_published.png -------------------------------------------------------------------------------- /images/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/cover.png -------------------------------------------------------------------------------- /images/cover_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/cover_thumb.png -------------------------------------------------------------------------------- /images/ec_over_small_prime_field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/ec_over_small_prime_field.png -------------------------------------------------------------------------------- /images/eip_workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/eip_workflow.png -------------------------------------------------------------------------------- /images/ens-checkname.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/ens-checkname.png -------------------------------------------------------------------------------- /images/ens-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/ens-flow.png -------------------------------------------------------------------------------- /images/etherscan_contract_address.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/etherscan_contract_address.png -------------------------------------------------------------------------------- /images/etherscan_withdrawal_internal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/etherscan_withdrawal_internal.png -------------------------------------------------------------------------------- /images/etherscan_withdrawal_tx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/etherscan_withdrawal_tx.png -------------------------------------------------------------------------------- /images/ganache_metoken.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/ganache_metoken.png -------------------------------------------------------------------------------- /images/ganache_metoken_transfer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/ganache_metoken_transfer.png -------------------------------------------------------------------------------- /images/hd_wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/hd_wallet.png -------------------------------------------------------------------------------- /images/http_ws_ipc_jsonrpc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/http_ws_ipc_jsonrpc.png -------------------------------------------------------------------------------- /images/metamask_account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/metamask_account.png -------------------------------------------------------------------------------- /images/metamask_account_context_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/metamask_account_context_menu.png -------------------------------------------------------------------------------- /images/metamask_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/metamask_download.png -------------------------------------------------------------------------------- /images/metamask_mnemonic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/metamask_mnemonic.png -------------------------------------------------------------------------------- /images/metamask_password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/metamask_password.png -------------------------------------------------------------------------------- /images/metamask_ropsten_faucet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/metamask_ropsten_faucet.png -------------------------------------------------------------------------------- /images/metamask_send_to_contract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/metamask_send_to_contract.png -------------------------------------------------------------------------------- /images/metamask_withdraw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/metamask_withdraw.png -------------------------------------------------------------------------------- /images/newproj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/newproj.png -------------------------------------------------------------------------------- /images/parity_deployment_confirmation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/parity_deployment_confirmation.png -------------------------------------------------------------------------------- /images/parity_txdemo_novalue_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/parity_txdemo_novalue_data.png -------------------------------------------------------------------------------- /images/parity_txdemo_novalue_nodata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/parity_txdemo_novalue_nodata.png -------------------------------------------------------------------------------- /images/parity_txdemo_value_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/parity_txdemo_value_data.png -------------------------------------------------------------------------------- /images/parity_txdemo_value_nodata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/parity_txdemo_value_nodata.png -------------------------------------------------------------------------------- /images/proxy-lib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/proxy-lib.png -------------------------------------------------------------------------------- /images/published_contract_transactions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/published_contract_transactions.png -------------------------------------------------------------------------------- /images/quickbuilds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/quickbuilds.png -------------------------------------------------------------------------------- /images/remix_close_tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/remix_close_tab.png -------------------------------------------------------------------------------- /images/remix_compile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/remix_compile.png -------------------------------------------------------------------------------- /images/remix_contract_address.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/remix_contract_address.png -------------------------------------------------------------------------------- /images/remix_contract_interact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/remix_contract_interact.png -------------------------------------------------------------------------------- /images/remix_create_contract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/remix_create_contract.png -------------------------------------------------------------------------------- /images/remix_faucet_load.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/remix_faucet_load.png -------------------------------------------------------------------------------- /images/remix_metamask_create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/remix_metamask_create.png -------------------------------------------------------------------------------- /images/remix_run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/remix_run.png -------------------------------------------------------------------------------- /images/remix_toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/remix_toolbar.png -------------------------------------------------------------------------------- /images/remix_withdraw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/remix_withdraw.png -------------------------------------------------------------------------------- /images/rlpx_rpc_xs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/rlpx_rpc_xs.png -------------------------------------------------------------------------------- /images/ropsten_block_explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/ropsten_block_explorer.png -------------------------------------------------------------------------------- /images/send_to_faucet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/send_to_faucet.png -------------------------------------------------------------------------------- /images/simple_elliptic_curve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/simple_elliptic_curve.png -------------------------------------------------------------------------------- /images/tarsier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/tarsier.png -------------------------------------------------------------------------------- /images/thanks.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/thanks.jpeg -------------------------------------------------------------------------------- /images/theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/theme.png -------------------------------------------------------------------------------- /images/web3suite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inoutcode/ethereum_book/c0bf454215ce25676d9cb5d8c852296753b293b0/images/web3suite.png -------------------------------------------------------------------------------- /前言.asciidoc: -------------------------------------------------------------------------------- 1 | [preface] 2 | [[preface_chap]] 3 | == 前言 4 | 5 | [[why_bees_sec]] 6 | === 封面上的蜜蜂有什么含义? 7 | 8 | 蜜蜂是一种展现出高度复杂行为的物种,最终使蜂巢受益。每个蜜蜂按照一套简单的规则自由运作,并通过“跳舞”来传达重要的结果。舞蹈带有重要的信息,比如太阳的位置和从蜂巢到目标的相对地理坐标。通过解读这种舞蹈,蜜蜂可以转播这些信息或采取行动,从而实现“蜂巢思维”的意志。 9 | 10 | 虽然蜜蜂形成了一个基于种群的社会,拥有繁殖后代的蜂后,但在蜂巢中没有中央权威或领导者。数千成员的种群表现出高度智能和复杂的行为,这社会网络中的个体相互交互而形成的“涌现”特性。 11 | 12 | 自然表明,去中心化的系统可以具有弹性,并且可以产生涌现的复杂性和令人难以置信的复杂性,而不需要中央机构,层级或复杂的部分。 13 | 14 | 15 | <<术语#,下一章:术语>> 16 | -------------------------------------------------------------------------------- /术语.asciidoc: -------------------------------------------------------------------------------- 1 | [preface] 2 | == 快速术语表 3 | 4 | <<前言#,上一章:前言>> 5 | 6 | 这个快速术语表包含许多与以太坊相关的术语。这些术语在本书中都有使用,所以请将其加入书签以便快速参考。 7 | 8 | 账户 Account:: 9 | 包含地址、余额、随机数以及可选存储和代码的对象。账户可以是合约账户或外部拥有账户(EOA,externally owned account). 10 | 11 | 地址 Address:: 12 | 一般来说,这代表一个 EOA 或合约,它可以在区块链上接收(目标地址)或发送(源地址)交易。更具体地说,它是 ECDSA 公钥的 Keccak 散列的最右边的160位,表现为16进制的40个字符长度,在前面加上“0x”字符。 13 | 14 | 断言 Assert:: 15 | 在 Solidity 中,assert(false) 编译为 *0xfe*, 是一个无效的操作码,用尽所有剩余的燃气(Gas),并恢复所有更改。 16 | 当 assert() 语句失败时,说明发生了严重的错误或异常,你必须修复你的代码。 17 | 你应该使用 assert 来避免永远不应该发生的条件。 18 | 19 | 大端序 Big-endian:: 20 | 一种数值的位置表示形式,最高有效位放在最前面。对应小端序(little-endian),最低有效位在前。 21 | 22 | 比特币改进提议 BIPs:: 23 | 比特币改进提议,Bitcoin Improvement Proposals。比特币社区成员提交的一组提案,旨在改进比特币。例如,BIP-21是改进比特币统一资源标识符(URI)方案的建议。 24 | 25 | 区块 Block:: 26 | 区块是关于所包含的交易的所需信息(区块头)的集合,以及称为ommer的一组其他区块头。它被矿工添加到以太坊网络中。 27 | 28 | 区块链 Blockchain:: 29 | 由工作证明系统验证的一系列区块,每个区块都连接到它的前任,一直到创世区块。这与比特币协议不同,因为它没有块大小限制;它改为使用不同的燃气限制。 30 | 31 | 拜占庭分叉 Byzantium Fork:: 32 | 拜占庭是大都会( Metropolis )发展阶段的两大分叉之一。它包括 EIP-649:大都会难度炸弹延迟和区块奖励减少,其中冰河时代(见下文)延迟1年,而区块奖励从5个以太坊减至3个以太坊。 33 | 34 | 编译 Compiling:: 35 | 将高级编程语言(例如 Solidity)编写的代码转换为低级语言(例如 EVM 字节码) 36 | 37 | 共识 Consensus:: 38 | 大量节点,通常是网络上的大多数节点,在其本地验证的最佳区块链中都有相同的区块的情况。 39 | 不要与共识规则混淆。 40 | 41 | 共识规则 Consensus rules:: 42 | 完整节点为了与其他节点保持一致,遵循的区块验证规则。不要与共识混淆。 43 | 44 | 君士坦丁堡 Constantinople:: 45 | 大都会阶段的第二部分,2018年中期的计划。预计将包括切换到混合工作证明/权益证明共识算法,以及其他变更。 46 | 47 | 合约账户 Contract account:: 48 | 包含代码的账户,每当它从另一个账户(EOA 或合约)收到交易时执行。 49 | 50 | 合约创建交易 Contract creation transaction:: 51 | 一个特殊的交易,以“零地址”作为收件人,用于注册合约并将其记录在以太坊区块链中(请参阅“零地址”)。 52 | 53 | 去中心化自治组织 DAO:: 54 | 去中心化自治组织 Decentralised Autonomous Organization. 没有层级管理的公司和其他组织。也可能是指2016年4月30日发布的名为“The DAO”的合约,该合约于2016年6月遭到黑客攻击,最终在第1,192,000个区块激起了硬分叉(代号 DAO),恢复了被攻击的 DAO 合约,并导致了以太坊和以太坊经典两个竞争系统。 55 | 56 | 去中心化应用 DApp:: 57 | 去中心化应用 Decentralised Application. 狭义上,它至少是智能合约和 web 用户界面。更广泛地说,DApp 是一个基于开放式,分散式,点对点基础架构服务的 Web 应用程序。另外,许多 DApp 包括去中心化存储和/或消息协议和平台。 58 | 59 | 契约 Deed:: 60 | ERC721提案中引入了不可替代的标记标准。与ERC20代币不同,契约证明了所有权并且不可互换,虽然它们还未在任何管辖区都被认可为合法文件,至少目前不是。 61 | 62 | 难度 Difficulty:: 63 | 网络范围的设置,控制产生工作量证明需要多少计算。 64 | 65 | 数字签名 Digital signature:: 66 | 数字签名算法是一个过程,用户可以使用私钥为文档生成称为“签名”的短字符串数据,以便具有签名,文档,和相应公钥的任何人,都可以验证(1 )该文件由该特定私钥的所有者“签名”,以及(2)该文件在签署后未被更改。 67 | 68 | 椭圆曲线数字签名算法 ECDSA:: 69 | 椭圆曲线数字签名算法( Elliptic Curve Digital Signature Algorithm,ECDSA )是以太坊用来确保资金只能由合法所有者使用的加密算法。 70 | 71 | 以太坊改进建议 EIP:: 72 | 以太坊改进建议,Ethereum Improvement Proposals,描述以太坊平台的建议标准。 EIP 是向以太坊社区提供信息的设计文档,描述新的功能,或处理过程,或环境。有关更多信息,请参见 https://github.com/ethereum/EIPs(另请参见下面的 ERC 定义)。 73 | 74 | 熵 Entropy:: 75 | 在密码学领域,表示可预测性的缺乏或随机性水平。在生成秘密信息(如主私钥)时,算法通常依赖高熵源来确保输出不可预测。 76 | 77 | 以太坊名称服务 ENS:: 78 | 以太坊名称服务,Ethereum Name Service. 更多信息,参见 https://github.com/ethereum/ens/. 79 | 80 | 外部拥有账户 EOA:: 81 | 外部拥有账户,Externally Owned Account. 由或为以太坊的真人用户创建的账户。 82 | 83 | 以太坊注释请求 ERC:: 84 | 以太坊注释请求 Ethereum Request for Comments. 一些 EIP 被标记为 ERC,表示试图定义以太坊使用的特定标准的建议。 85 | 86 | Ethash:: 87 | 以太坊1.0的工作量证明算法。更多信息,参见 https://github.com/ethereum/wiki/wiki/Ethash. 88 | 89 | 以太 Ether:: 90 | 以太 Ether,是以太坊生态系统中使用的本地货币,在执行智能合约时承担燃气费用。它的符合是 Ξ, 极客用的大写 Xi 字符. 91 | 92 | Event:: 93 | 事件允许EVM日志工具的使用,后者可以用来在DApp的用户界面中调用JavaScript回调来监听这些事件。更多信息,参见 http://solidity.readthedocs.io/en/develop/contracts.html#events。 94 | 95 | 以太坊虚拟机 EVM:: 96 | Ethereum Virtual Machine, 基于栈的,执行字节码的虚拟机。在以太坊中,执行模型指定了系统状态如何在给定一系列字节码指令和少量环境数据的情况下发生改变。 97 | 这是通过虚拟状态机的正式模型指定的。 98 | 99 | EVM汇编语言 EVM Assembly Language:: 100 | 字节码的人类可读形式。 101 | 102 | 后备方法 Fallback function:: 103 | 默认的方法,当缺少数据或声明的方法名时执行。 104 | 105 | 水龙头 Faucet:: 106 | 一个网站,为想要在testnet上做测试的开发人员提供免费测试以太形式的奖励。 107 | 108 | 前沿 Frontier:: 109 | 以太坊的试验开发阶段,从2015年7月至2016年3月。 110 | 111 | Ganache:: 112 | 私有以太坊区块链,你可以在上面进行测试,执行命令,在控制区块链如何运作时检查状态。 113 | 114 | 燃气 Gas:: 115 | 以太坊用于执行智能合约的虚拟燃料。以太坊虚拟机使用会计机制来衡量天然气的消耗量并限制计算资源的消耗。参见“图灵完备”。 116 | 燃气是执行智能合约的每条指令产生的计算单位。燃气与以太加密货币挂钩。燃气类似于蜂窝网络上的通话时间。因此,以法定货币进行交易的价格是 gas *(ETH /gas)*(法定货币/ETH)。 117 | 118 | 燃气限制 Gas limit:: 119 | 在谈论区块时,它们也有一个名为燃气限制的区域。它定义了整个区块中所有交易允许消耗的最大燃气量。 120 | 121 | 创世区块 Genesis block:: 122 | 区块链中的第一个块,用来初始化特定的网络和加密数字货币。 123 | 124 | Geth:: 125 | Go语言的以太坊。Go编写的最突出的以太坊协议实现之一。 126 | 127 | 硬分叉 Hard fork:: 128 | 硬分叉也称为硬分叉更改,是区块链中的一种永久性分歧,通常发生在非升级节点无法验证升级节点创建的遵循新共识规则的区块时。不要与分叉,软分叉,软件分叉或Git分叉混淆。 129 | 130 | 哈希值 Hash:: 131 | 通过哈希方法为可变大小的数据生成的固定长度的指纹。 132 | 133 | 分层确定钱包 HD wallet:: 134 | 使用分层确定密钥生成和传输协议的钱包(BIP32)。 135 | 136 | 分层确定钱包种子 HD wallet seed:: 137 | HD钱包种子或根种子是一个可能很短的值,用作生成HD钱包的主私钥和主链码的种子。钱包种子可以用助记词表示,使人们更容易复制,备份和恢复私钥。 138 | 139 | 家园 Homestead:: 140 | 以太坊的第二个发展阶段,于2016年3月在1,150,000区块启动。 141 | 142 | 冰河时代 Ice Age:: 143 | 以太坊在200,000区块的硬分叉,提出难度指数级增长(又名难度炸弹),引发了到权益证明Proof-of-Stake的过渡。 144 | 145 | 集成开发环境 IDE (Integrated Development Environment):: 146 | 集成的用户界面,结合了代码编辑器、编译器、运行时和调试器。 147 | 148 | 不可变的部署代码问题 Immutable Deployed Code Problem:: 149 | 一旦部署了契约(或库)的代码,它就成为不可变的。修复可能的bug并添加新特性是软件开发周期的关键。这对智能合约开发来说是一个挑战。 150 | 151 | 互换客户端地址协议 Inter exchange Client Address Protocol (ICAP):: 152 | 以太坊地址编码,与国际银行帐号(IBAN)编码部分兼容,为以太坊地址提供多样的,校验和的,可互操作的编码。 ICAP地址可以编码以太坊地址或通过以太坊名称注册表注册的常用名称。他们总是以XE开始。其目的是引入一个新的IBAN国家代码:XE,X表示"extended", 加上以太坊的E,用于非管辖货币(例如XBT,XRP,XCP)。 153 | 154 | 内部交易(又称“消息”)Internal transaction (also "message"):: 155 | 从一个合约地址发送到另一个合约地址或EOA的交易。 156 | 157 | Keccak256:: 158 | 以太坊使用的加密哈希方法。虽然在早期 Ethereum 代码中写作 SHA-3,但是由于在 2015 年 8 月 SHA-3 完成标准化时,NIST 调整了填充算法,所以 Keccak256 不同于标准的 NIST-SHA3。Ethereum 也在后续的代码中开始将 SHA-3 的写法替换成 Keccak256 。 159 | 160 | 密钥推导方法 Key Derivation Function (KDF):: 161 | 也称为密码扩展算法,它被keystore格式使用,以防止对密码加密的暴力破解,字典或彩虹表攻击。它重复对密码进行哈希。 162 | 163 | Keystore 文件:: 164 | JSON 编码的文件,包含一个(随机生成的)私钥,被一个密码加密,以提供额外的安全性。 165 | 166 | LevelDB:: 167 | LevelDB是一种开源的磁盘键值存储系统。LevelDB是轻量的,单一目标的持久化库,支持许多平台。 168 | 169 | 库 Library:: 170 | 以太坊中的库,是特殊类型的合约,没有用于支付的方法,没有后备方法,没有数据存储。所以它不能接收或存储以太,或存储数据。库用作之前部署的代码,其他合约可以调用只读计算。 171 | 172 | 轻量级客户端 Lightweight client:: 173 | 轻量级客户端是一个以太坊客户端,它不存储区块链的本地副本,也不验证块和事务。它提供了钱包的功能,可以创建和广播交易。 174 | 175 | 消息 Message:: 176 | 内部交易,从未被序列化,只在EVM中发送。 177 | 178 | 大都会阶段 Metropolis Stage:: 179 | 大都会是以太坊的第三个开发阶段,在2017年10月启动。 180 | 181 | METoken:: 182 | Mastering Ethereum Token. 本书中用于演示的 ERC20 代币。 183 | 184 | 矿工 Miner:: 185 | 通过重复哈希计算,为新的区块寻找有效的工作量证明的网络节点。 186 | 187 | Mist:: 188 | Mist是以太坊基金会创建的第一个以太坊浏览器。它还包含一个基于浏览器的钱包,这是ERC20令牌标准的首次实施(Fabian Vogelsteller,ERC20的作者也是Mist的主要开发人员)。Mist也是第一个引入camelCase校验码(EIP-155)的钱包。Mist运行完整节点,提供完整的DApp浏览器,支持基于Swarm的存储和ENS地址 189 | 190 | 网络 Network:: 191 | 将交易和区块传播到每个以太坊节点(网络参与者)的对等网络。 192 | 193 | 节点 Node:: 194 | 参与到对等网络的软件客户端。 195 | 196 | 随机数 Nonce:: 197 | 密码学中,随机数指代只可以用一次的数值。在以太坊中用到两类随机数。 198 | - 账户随机数 - 这只是一个账户的交易计数。 199 | - 工作量证明随机数- 用于获得工作证明的区块中的随机值(取决于当时的难度)。 200 | 201 | Ommer:: 202 | 祖父节点的子节点,但它本身并不是父节点。当矿工找到一个有效的区块时,另一个矿工可能已经发布了一个竞争的区块,并添加到区块链顶部。像比特币一样,以太坊中的孤儿区块可以被新的区块作为ommers包含,并获得部分奖励。术语 "ommer" 是对父节点的兄弟姐妹节点的性别中立的称呼,但也可以表示为“叔叔”。 203 | 204 | Parity:: 205 | 以太坊客户端软件最突出的支持共同操作(多重签名)的实现之一。 206 | 207 | 权益证明 Proof-of-Stake (PoS):: 208 | 权益证明是加密货币区块链协议旨在实现分布式共识的一种方法。权益证明要求用户证明一定数量的加密货币(网络中的“股份”)的所有权,以便能够参与交易验证。 209 | 210 | 工作量证明 Proof-of-Work (PoW):: 211 | 一份需要大量计算才能找到的数据(证明)。在以太坊,矿工必须找到符合网络难度目标的 Ethash 算法的数字解决方案。 212 | 213 | 收据 Receipt:: 214 | 以太坊客户端返回的数据,表示特定交易的结果,包括交易的哈希值,其区块编号,使用的燃气量,以及在部署智能合约时的合约地址。 215 | 216 | 重入攻击 Re-entrancy Attack:: 217 | 当攻击者合约(Attacker contracts)调用受害者合约(Victim contracts)的方法时,可以重复这种攻击。让我们称它为victim.withdraw(),在对该合约函数的原始调用完成之前,再次调用victim.withdraw()方法,持续递归调用它自己。 218 | 递归调用可以通过攻击者合约的后备方法实现。 219 | 攻击者必须执行的唯一技巧是在用完燃气之前中断递归调用,并避免盗用的以太被还原。 220 | 221 | Require:: 222 | 在Solidity中,require(false)编译为 *0xfd*,它是 *REVERT* 操作码。REVERT指令提供了一种停止执行和恢复状态更改的方式,不消耗所有提供的燃气并且能够返回原因。 223 | 应使用require函数来确保满足有效条件,如输入或合同状态变量,或者验证调用外部合约的返回值。 224 | 在*拜占庭*网络升级之前,有两种实际的方式来还原交易:耗尽燃气或执行无效指令。这两个选项都消耗了所有剩余的气体。 225 | 在*Byzantium*网络升级之前,在*黄皮书*中无法找到此操作码,并且因为该操作码没有规范,所以当EVM执行到它时,会抛出一个 _invalid opcode error_。 226 | 227 | 还原 Revert:: 228 | 当需要处理与 <> 相同的情况,但使用更复杂的逻辑时,使用 revert()。 229 | 例如,如果你的代码有一些嵌套的 if/else 逻辑流程,你会发现使用 <> 而不是require()是合理的。 230 | 231 | 奖励 Reward:: 232 | Ether(ETH)的数量,包含在每个新区块中的金额作为网络对找到工作证明解决方案的矿工的奖励。 233 | 234 | 递归长度前缀 Recursive Length Prefix (RLP):: 235 | RLP 是一种编码标准,由以太坊开发人员设计用来编码和序列化任意复杂度和长度的对象(数据结构)。 236 | 237 | 中本聪 Satoshi Nakamoto:: 238 | Satoshi Nakamoto 是设计比特币及其原始实现 Bitcoin Core 的个人或团队的名字。作为实现的一部分,他们也设计了第一个区块链。在这个过程中,他们是第一个解决数字货币的双重支付问题的。他们的真实身份至今仍是个谜。 239 | 240 | Vitalik Buterin:: 241 | Vitalik Buterin 是俄国-加拿大的程序员和作家,以太坊和 Bitcoin 杂志的联合创始人。 242 | 243 | Gavin Wood:: 244 | Gavin Wood 是英国的程序员,以太坊的联合创始人和前 CTO。在2014年8月他提出了Solidity,用于编写智能合约的面向合约的编程语言。 245 | 246 | 密钥(私钥) Secret key (aka private key):: 247 | 允许以太坊用户通过创建数字签名(参见公钥,地址,ECDSA)证明账户或合约的所有权的加密数字。 248 | 249 | SHA:: 250 | 安全哈希算法 Secure Hash Algorithm,SHA 是美国国家标准与技术研究院(NIST)发布的一系列加密哈希函数。 251 | 252 | SELFDESTRUCT 操作码:: 253 | 只要整个网络存在,智能合同就会存在并可执行。如果它们被编程为自毁的或使用委托调用(delegatecall)或调用代码(callcode)执行该操作,它们将从区块链中消失。 254 | 一旦执行自毁操作,存储在合同地址处的剩余Ether将被发送到另一个地址,并将存储和代码从状态中移除。 255 | 尽管这是预期的行为,但自毁合同的修剪可能或不会被以太坊客户实施。 256 | SELFDESTRUCT 之前称作 SUICIDE, 在EIP6中, SUICIDE 重命名为 SELFDESTRUCT。 257 | 258 | 宁静 Serenity:: 259 | 以太坊第四个也是最后一个开发阶段。宁静还没有计划发布的日期。 260 | 261 | Serpent:: 262 | 语法类似于 Python 的过程式(命令式)编程语言。也可以用来编写函数式(声明式)代码,尽管它不是完全没有副作用的。首先由 Vitalik Buterin 创建。 263 | 264 | 智能合约 Smart Contract:: 265 | 在以太坊的计算框架上执行的程序。 266 | 267 | Solidity:: 268 | 过程式(命令式)编程语言,语法类似于 Javascript, C++ 或 Java。以太坊智能合约最流行和最常使用的语言。由 Gavin Wood(本书的联合作者)首先创造 269 | 270 | Solidity inline assembly:: 271 | 内联汇编 Solidity 中包含的使用 EVM 汇编(EVM 代码的人类可读形式)的代码。内联汇编试图解决手动编写汇编时遇到的固有难题和其他问题。 272 | 273 | Spurious Dragon:: 274 | 在#2,675,00块的硬分叉,来解决更多的拒绝服务攻击向量,以及另一种状态清除。还有转播攻击保护机制。 275 | 276 | Swarm:: 277 | 一种去中心化(P2P)的存储网络。与 Web3 和 Whisper 共同使用来构建 DApps。 278 | 279 | Tangerine Whistle:: 280 | 在 #2,463,00 块的硬分叉,改变了某些 I/O 密集操作的燃气计算方式,并从拒绝服务攻击中清除累积状态,这种攻击利用了这些操作的低燃气成本。 281 | 282 | 测试网 Testnet:: 283 | 一个测试网络(简称 testnet),用于模拟以太网主要网络的行为。 284 | 285 | 交易 Transaction:: 286 | 由原始帐户签署的提交到以太坊区块链的数据,并以特定地址为目标。交易包含元数据,例如交易的燃气限额。 287 | 288 | Truffle:: 289 | 一个最常用的以太坊开发框架。包含一些 NodeJS 包,可以使用 Node Package Manager (NPM) 安装。 290 | 291 | 图灵完备 Turing Complete:: 292 | 在计算理论中,如果数据操纵规则(如计算机的指令集,程序设计语言或细胞自动机)可用于模拟任何图灵机,则它被称为图灵完备或计算上通用的。这个概念是以英国数学家和计算机科学家阿兰图灵命名的。 293 | 294 | Vyper:: 295 | 一种高级编程语言,类似 Serpent,有 Python 式的语法,旨在接近纯函数式语言。由 Vitalik Buterin 首先创造。 296 | 297 | 钱包 Wallet:: 298 | 拥有你的所有密钥的软件。作为访问和控制以太坊账户并与智能合约交互的界面。请注意,密钥不需要存储在你的钱包中,并且可以从脱机存储(例如 USB 闪存驱动器或纸张)中检索以提高安全性。尽管名字为钱包,但它从不存储实际的硬币或代币。 299 | 300 | Web3:: 301 | web 的第三个版本。有 Gavin Wood 首先提出,Web3 代表了 Web 应用程序的新愿景和焦点:从集中拥有和管理的应用程序到基于去中心化协议的应用程序。 302 | 303 | Wei:: 304 | 以太的最小单位,10^18^ wei = 1 ether. 305 | 306 | Whisper:: 307 | 一种去中心化(P2P)消息系统。与 Web3 和 Swarm 一起使用来构建 DApps。 308 | 309 | 零地址 Zero address:: 310 | 特殊的以太坊地址,全部是由 `0` 组成(即 `0x0000000000000000000000000000000000000000`),被指定为创建一个智能合约所发起的交易(Transaction)的目标地址(即 `to` 参数的值)。 311 | 312 | 313 | <<第一章#,下一章:什么是以太坊>> 314 | 315 | image::images/thanks.jpeg["赞赏译者",height=400,align="center"] 316 | -------------------------------------------------------------------------------- /第十一章.asciidoc: -------------------------------------------------------------------------------- 1 | <<第十章#,上一章:Tokens>> 2 | 3 | [[decentralized_applications_chap]] 4 | == 去中心化应用 (DApps) 5 | 6 | 点对点(P2P,peer-to-peer)运动使数百万互联网用户能够连接在一起。USENET是被称为第一个点对点架构的一种分布式消息传递系统,它于1979年成立,是第一个“互联网”ARPANET的继承者。ARPANET是一个客户端 - 服务器网络,参与者运行节点请求和提供内容,但由于除了简单的基于地址的路由,缺乏提供任何上下文的能力,USENET很有希望实施一个分散的控制模型,即客户端 - 服务器模型,分别从用户或客户角度为新闻组服务器提供自组织方法。 7 | 8 | 1999年,著名的音乐和文件共享应用程序Napster出现了。Napster是点对点网络运动演变为BitTorrent的开始,参与用户建立了一个虚拟网络,完全独立于物理网络,无需遵守任何管理机构或限制。 9 | 10 | 由于点对点机制可用于访问任何类型的分布式资源,因此它们在去中心化应用程序中起着核心作用。 11 | 12 | [[what_is_a_dapp_sec]] 13 | === 什么是DApp? 14 | 15 | 与传统应用程序不同,去中心化应用(DApp)不仅属于单个提供者或服务器,而是整个栈将在P2P网络上以分布式方式部署和操作。 16 | 17 | 典型的DApp栈包括前端,后端和数据存储。创建DApp有许多优点,典型集中式架构无法提供: 18 | 19 | 1)弹性:在智能合约上编写业务逻辑意味着DApp后端将在区块链上完全分发和管理。与在中央服务器上部署应用程序不同,DApp不会有停机时间,只要区块链仍在运行,它就会继续存在。 20 | 21 | 2)透明性:DApp的开源特性允许任何人分叉代码并在区块链上运行相同的应用程序。同样,任何与区块链的互动都将永久存储,任何拥有区块链副本的人都可以获得对它的访问权限。值得注意的是,可能无法将字节码反编译为源码并完全理解合约的代码。寻求提供合约行为完全透明的开发人员必须发布供用户阅读,编译和验证的源代码。 22 | 23 | 3)抗审查:只要用户可以访问以太坊节点,用户将始终能够与DApp交互而不受集中机构控制的干扰。一旦在网络上部署代码,任何服务提供商,甚至智能合约的所有者都不能更改代码。 24 | 25 | [[components_of_a_dapp_sec]] 26 | === DApp的组件 27 | 28 | [[blockchain_smart_contracts_sec]] 29 | ==== 区块链(智能合约) 30 | 31 | 智能合约用于存储去中心化应用程序的业务逻辑,状态和计算; 将智能合约视为常规应用程序中的服务器端组件。 32 | 33 | 在以太坊智能合约上部署服务器端逻辑的一个优点是,你可以构建一个更复杂的架构,智能合约可以在其中相互读取和写入数据。部署智能合约后,未来许多其他开发人员都可以使用你的业务逻辑,而无需你管理和维护代码。 34 | 35 | 将智能合约作为核心业务逻辑功能运行的一个主要问题是在部署代码后无法更改代码。此外,一个非常庞大的智能合约可能需要耗费大量gas来部署和运行。因此,某些应用程序可能会选择离线计算和外部数据源。请记住,DApp的核心业务逻辑依赖于外部数据或服务器意味着你的用户必须信任这些外部事物。 36 | 37 | [[front_end_web_ui_cec]] 38 | ==== 前端(Web用户界面(UI)) 39 | 40 | 与DApp的业务逻辑需要开发人员了解EVM和新语言(如Solidity)不同,DApp的客户端界面使用基本的Web前端技术(HTML,CSS,JavaScript)。这允许传统的Web开发人员使用他们熟悉的工具,库和框架。与DApp的交互(例如签名消息,发送交易和密钥管理)通常通过浏览器本身使用Mist浏览器或Metamask浏览器扩展等工具进行。 41 | 42 | 虽然也可以创建移动DApp,但由于缺少可用作具有密钥管理功能的轻客户端的移动客户端,目前没有创建移动DApp前端的最佳实践。 43 | 44 | [[data_storage_sec]] 45 | ==== 数据存储 46 | 47 | 由于gas成本高,智能合约目前不适合存储大量数据。因此,大多数DApps将利用去中心化存储(如IPFS或Swarm)来存储和分发大型静态资产,如图像,视频和客户端应用程序(HTML,CSS,JavaScript)。 48 | 49 | 内容的哈希值通常使用键值映射存储为智能合约中的字节。然后,通过你的前端应用程序调用智能合约检索资产,以获取每个资产的URL。 50 | 51 | [[on_chain_vs_off_chain_data_sec]] 52 | ===== 上链 vs. 脱链 53 | 54 | //// 55 | TODO 56 | //// 57 | 58 | [[ipfs_sec]] 59 | ===== IPFS 60 | 61 | //// 62 | TODO 63 | //// 64 | 65 | [[swarm_sec]] 66 | ===== Swarm 67 | 68 | Swarm主页: http://swarm-gateways.net/bzz:/theswarm.eth/ 69 | 70 | 阅读文档: https://swarm-guide.readthedocs.io/en/latest/index.html 71 | 72 | Swarm开发人员的入门指南: https://github.com/ethersphere/swarm/wiki/swarm 73 | 74 | Swarm讨论组: https://gitter.im/ethersphere/orange-lounge 75 | 76 | Ethereum's Swarm 和 IPFS 的相似之处与不同之处; https://github.com/ethersphere/go-ethereum/wiki/IPFS-&-SWARM 77 | 78 | [[centralized_db_sec]] 79 | ===== 中心化数据 80 | 81 | 集中式数据库是服务器上的数据存储,其特征与描述相关联。它们使用客户端 - 服务器网络架构,这允许客户端或用户修改存储在集中式服务器上的数据。数据库的控制权仍由指定的管理员进行,管理员在提供对数据库的访问之前对客户端的凭据进行身份验证。如果管理员的安全性受到损害,则可以更改甚至删除数据,因为管理员是唯一负责管理数据库的人。 82 | 83 | [[dapp_frameworks_sec]] 84 | === DApp框架 85 | 86 | 有许多不同的开发框架和库,以多种语言编写,使得开发人员可以在创建和部署DApp时获得更好的体验。 87 | 88 | [[truffle_sec]] 89 | ==== Truffle 90 | 91 | Truffle是一种流行的选择,为以太坊提供可管理的开发环境,测试框架和资产管道。 92 | 93 | 有了Truffle,你会得到: 94 | 95 | * 内置智能合约编译,链接,部署和二进制管理。 96 | * 与Mocha和Chai进行自动合约测试。 97 | * 可配置的构建管道,支持自定义构建过程。 98 | * 可编写脚本的部署和迁移框架。 99 | * 用于部署到许多公共和专用网络的网络管理。 100 | * 直接与合约沟通的交互式控制台。 101 | * 在开发过程中即时重建资产。 102 | * 在Truffle环境中执行脚本的外部脚本运行器。 103 | 104 | 入门和文档:http://truffleframework.com/docs 105 | 106 | Github:https://github.com/trufflesuite/truffle 107 | 108 | Website:https://truffleframework.com 109 | 110 | [[embark_sec]] 111 | ==== Embark Embark框架专注于使用以太坊,IPFS和其他平台的无服务器去中心化应用。Embark目前与EVM区块链(Ethereum),去中心化存储(IPFS)和去中心化通信平台(Whisper和Orbit)集成。 112 | 113 | ** 区块链(以太坊) 114 | * 自动部署合约并使其在JS代码中可用。启动监视更改,如果你更新合约,Embark将自动重新部署合约(如果需要)和DApp。 115 | * JS通过Promises使用合约。 116 | * 使用Javascript与合约进行测试驱动开发。 117 | * 跟踪已部署的合约; 只在真正需要时部署。 118 | * 管理不同的链(例如,测试网,私人网,livenet) 119 | * 轻松管理相互依赖合约的复杂系统。 120 | 121 | ** 去中心化存储(IPFS) 122 | * 通过EmbarkJS轻松存储和检索DApp上的数据,包括上传和检索文件。 123 | * 将完整的应用程序部署到IPFS或Swarm。 124 | 125 | ** 去中心化通信 (Whisper, Orbit) 126 | * 通过Whisper或Orbit轻松通过P2P渠道发送/接收消息。 127 | 128 | ** 网络技术 129 | * 与任何网络技术集成,包括React,Foundation等。 130 | * 使用你想要的任何构建管道或工具,包括grunt,gulp和webpack。 131 | 132 | 入门和文档:https://embark.readthedocs.io 133 | 134 | Github:https://github.com/embark-framework/embark 135 | 136 | Website:https://github.com/embark-framework/embark 137 | 138 | ==== Emerald 139 | 140 | Emerald Platform 是一个框架和工具集,用于简化Dapps的开发以及现有服务与基于以太坊的区块链的集成。 141 | 142 | Emerald提供: 143 | 144 | * Javascript库和React组件构建Dapp 145 | * 区块链项目常见的SVG图标 146 | * 用于管理私钥的Rust库,包括硬件钱包和签名交易 147 | * 可以集成到现有app命令行或JSON RPC API中的现成的组件和服务 148 | * SputnikVM,一个独立的EVM实现,可用于开发和测试 149 | 150 | 它与平台无关,为各种目标提供工具: 151 | 152 | * 与Electron捆绑的桌面应用程序 153 | * 移动应用程序 154 | * 网络应用程序 155 | * 命令行应用程序和脚本工具 156 | 157 | 入门和文档:https://docs.etcdevteam.com 158 | 159 | Github:https://github.com/etcdevteam/emerald-platform 160 | 161 | Website:https://emeraldplatform.io 162 | 163 | [[dapp_develotment_tool_sec] 164 | ==== DApp(开发工具) 165 | DApp是一个用于智能合约开发的简单命令行工具。它支持以下常见用例: 166 | 167 | * 包管理 168 | * 源代码构建 169 | * 单元测试 170 | * 简单的合约部署 171 | 172 | 入门和文档:https://dapp.readthedocs.io/en/latest/ 173 | 174 | [[live_dapps_sec]] 175 | === 活跃的DApps 176 | 177 | 以下列出了以太坊网络上的活跃DApp: 178 | 179 | [[ethpm_sec]] 180 | ==== EthPM 181 | 一个旨在将包管理带入以太坊生态系统的项目。 182 | 183 | Website:https://www.ethpm.com/ 184 | 185 | [[radar_relay_sec]] 186 | ==== Radar Relay 187 | DEX(去中心化交易所)专注于直接从钱包到钱包交易基于以太坊的tokens。 188 | 189 | Website:https://radarrelay.com/ 190 | 191 | [[cryptokitties_sec]] 192 | ==== CryptoKitties 193 | 在以太坊上部署的游戏,允许玩家购买,收集,繁殖和销售各种类型的虚拟猫 194 | 它代表了为休闲和悠闲目的部署区块链技术的最早尝试之一。 195 | 196 | Website:https://www.cryptokitties.co 197 | 198 | [[ethlance_sec]] 199 | ==== Ethlance 200 | Ethlance是一个连接自由职业者和开发者的平台,用ether支付和收款。 201 | 202 | Website:https://ethlance.com/ 203 | 204 | [[decentraland_sec]] 205 | ==== Decentraland 206 | Decentraland是以太坊区块链支持的虚拟现实平台。用户可以创建,体验内容和应用程序并从中获利。 207 | 208 | Website:https://decentraland.org/ 209 | 210 | <<第十二章#,下一章:Oracles>> 211 | 212 | image::images/thanks.jpeg["赞赏译者",height=400,align="center"] 213 | -------------------------------------------------------------------------------- /第十七章.asciidoc: -------------------------------------------------------------------------------- 1 | <<第十六章#,上一章:Vyper:面向合约的编程语言>> 2 | 3 | [[communications_between_nodes]] 4 | == 节点间的通信 —— 一个简单的视角 5 | 6 | 以太坊节点之间通过简单的线路协议进行通信,形成一个虚拟或覆盖良好的网络 7 | 为实现这一目标,该协议称为*ÐΞVp2p*,使用*RLP*等技术和标准。 8 | 9 | [[transport_protocol]] 10 | === 传输协议 11 | 12 | 为了提供机密性并防止网络中断,*ÐΞVp2p*节点使用*RLPx*消息,一种加密且经过身份验证的_transport协议。 13 | *RLPx*使用类似于*Kademlia*的路由算法,*Kademlia*是用于分散的对等计算机网络的分布式哈希表(* DHT *)。 14 | *RLPx*,作为底层传输协议,允许_“节点发现和网络形成”_。 15 | *RLPx*的另一个显著特征是通过单个连接支持_多个协议_。 16 | 17 | 当*ÐΞVp2p*节点通过Internet进行通信时(通常情况下),它们使用TCP,它提供面向连接的介质,但实际上*ÐΞVp2p*节点通过使用底层传输协议*RLPx*所提供的所谓设施(或消息),以数据包通信,允许它们通信发送和接收数据包。 18 | 19 | 数据包是 _动态构建_ _dynamicically framed_,前缀为_RLP_编码标头,经过加密和验证。通过帧头实现多路复用,帧头指定分组的目的协议。 20 | 21 | ==== 加密握手 22 | 23 | 通过握手建立连接,并且一旦建立,就将数据包加密并封装为帧。 24 | 25 | 此握手将分两个阶段进行,第一阶段涉及密钥交换,第二阶段将执行身份验证,作为*DEVp2p*的一部分,还将交换每个节点的功能。 26 | 27 | ==== 安全 - 基本考虑因素 28 | 29 | 所有加密操作都基于*secp256k1*,并且每个节点都应该维护一个静态私钥,该私钥在会话之间保存和恢复。 30 | 31 | 在实施加密之前,数据包具有时间戳属性,以减少执行重放攻击的时间窗口。 32 | 建议接收方只接受最近3秒内创建的数据包。 33 | 34 | 数据包被签名。通过从签名中恢复公钥并检查它是否与预期值匹配来执行验证。 35 | 36 | [[devp2p_messages_subprotocols]] 37 | === ÐΞVp2p 消息和子协议 38 | 使用*RLP*,我们可以编码不同类型的数据,其类型由RLP的第一个条目中使用的整数值确定。 39 | 这样,*ÐΞVp2p*,_基础线路协议_ _basic wire protocol_,支持_任意的子协议_。 40 | 41 | `0x00-0x10`之间的_Message IDs_保留用于*ÐΞVp2p*消息。因此,假定_sub-protocols_的消息ID从“0x10”开始。 42 | 43 | 未在对等节点之间共享的子协议是_忽略的_。 44 | 如果共享相同(同名)子协议的多个版本,则数字最高的胜出。 45 | 46 | ==== 基本建立通信 - 基本ÐΞVp2p消息 47 | 48 | 作为一个非常基本的例子,当两个对等节点发起他们的通信时,每个对等节点用另一个称为*“Hello”*的特殊*ÐΞVp2p*消息来迎接另一个,该消息由“0x00”消息ID标识。 49 | 通过这个特定的*ÐΞVp2p* *“Hello”*消息,每个节点将向其对等的相关数据公开,从而允许通信以非常基本的级别开始。 50 | 51 | 在此步骤中,每个对等方将知道有关其对等方的以下信息。 52 | 53 | - P2P协议的实现*版本*。现在必须是'1`。 54 | - *客户端软件标识*,作为人类可读的字符串(例如`Ethereum(++)/ 1.0.0`)。 55 | - 对等节点的*capability name*为长度为3的ASCII字符串。当前支持的能力名称为“eth”和“shh”。 56 | - 对等节点的*capability version*为正整数。目前支持的版本是`eth`为`34`,`shh`为`1`。 57 | - 客户端正在侦听的*端口*。如果为“0”则表示客户端没有收听。 58 | - *节点的唯一标识*指定为512位散列。 59 | 60 | ==== 断开连接 - 基本ÐΞVp2p消息 61 | 要执行有序的断开连接,要断开连接的节点将发送名为*“Disconnect”*的*ÐΞVp2p*消息,该消息由_“0x01”_消息ID标识。此外,节点使用参数*“reason”*指定断开的原因。 62 | 63 | * “reason”*参数可以取值从“0x00”到“0x10”,例如“0x00”表示原因*“请求断开连接”*和“0x04”表示*“太多对等节点”*。 64 | 65 | ==== 状态 - 以太坊子协议示例 66 | 67 | 该子协议由`+0x00`消息-id标识。 68 | 69 | 此消息应在初始握手之后和任何与以太坊相关的消息之前发送,并通知其当前状态。 70 | 71 | 为此,节点向其对等方公开以下数据; 72 | 73 | - *Protocol version* 74 | - *Network Id* 75 | - *Total Difficulty of the best chain* 76 | - *Hash of the best known block* 77 | - *Hash of the Genesis block* 78 | 79 | [[known_current_networks]] 80 | ===== 已知的当前网络ID 81 | 这里是目前已知的网络ID列表: 82 | 83 | - 0: *Olympic*; 以太坊公共预发布测试网 84 | - 1: *Frontier*; Homestead,Metropolis,以太坊公共主网 85 | - 1: *Classic*; (un)forked 公共以太坊Classic主网络,链ID 61 86 | - 1: *Expanse*; 另一个以太坊实现,链ID 2 87 | - 2: *Morden*; 公共以太坊测试网,现在是以太坊经典测试网 88 | - 3: *Ropsten*; 公共跨客户端以太坊测试网 89 | - 4: *Rinkeby*: 公共Geth以太坊测试网 90 | - 42: *Kovan*; 公共Parity以太坊测试网 91 | - 77: *Sokol*; 公共POA测试网 92 | - 99: *POA*; 公共权威证明(PoA)以太网网络 93 | - 7762959: *Musicoin*; 音乐区块链 94 | 95 | ==== GetBlocks - 另一个子协议示例 96 | 该子协议由`+ 0x05` message-id标识。 97 | 98 | 通过此消息,节点通过其自己的哈希向其对等方请求指定的块。 99 | 100 | 请求节点的方式是通过包含它们所有哈希值的列表,将消息采用以下形式; 101 | 102 | .... 103 | [+0x05: P, hash_0: B_32, hash_1: B_32, ...] 104 | .... 105 | 106 | 请求节点必须没有包含所有请求的块的响应消息,在这种情况下,它必须再次请求那些尚未由其对等方发送的消息。 107 | 108 | === 节点标识和声誉 109 | *ÐΞVp2p*节点的标识是*secp256k1*公钥。 110 | 111 | 客户端可以自由标记新节点并使用节点ID作为_决定节点的信誉_的方法。 112 | 113 | 他们可以存储给定ID的评级并相应地给予优先权。 114 | 115 | <<第十八章#,下一章:以太坊标准>> 116 | 117 | image::images/thanks.jpeg["赞赏译者",height=400,align="center"] 118 | -------------------------------------------------------------------------------- /第十三章.asciidoc: -------------------------------------------------------------------------------- 1 | <<第十二章#,上一章:Oracles>> 2 | 3 | [[gas]] 4 | == gas(Gas) 5 | 6 | **Gas**是以太坊用于衡量程序执行一个或一组动作所需计算量的单位。交易或合约执行的每项操作都需要一定数量的gas; 所需的gas数量与正在执行的计算步骤的类型和数量有关。与仅以千字节(kB)计算交易规模的比特币交易费相比,以太坊交易费必须考虑智能合约代码可以执行的任意数量的计算步骤。程序执行的操作数越多,运行完成的成本就越高。 7 | 8 | 每次操作都需要固定量的gas。以太坊黄皮书的的一些例子: 9 | 10 | * 添加两个数字需要3个gas 11 | * 计算Keccak256哈希值,需要30个gas+ 每256位数据被哈希6个gas 12 | * 发送交易成本为21000 gas 13 | 14 | gas是以太坊的重要组成部分,具有双重作用。一,作为以太坊价格(具有波动性)和矿工对其工作的奖励之间的抽象层。另一种是抵御拒绝服务攻击。为了防止网络中的意外或恶意无限循环或其他计算浪费,每个交易的发起者需要设置他们愿意花费在gas上的金额的限制。因此,gas系统阻止攻击者发送垃圾邮件交易,因为他们必须按比例支付他们消耗的计算,带宽和存储资源。 15 | 16 | === 停机问题 17 | 18 | 交易费和会计的想法似乎很实用,但你可能想知道为什么以太坊首先需要gas。gas是关键,因为它不仅可以解决停机问题,而且对安全和活力也至关重要。什么是停机问题,安全和活力,为什么要关心? 19 | 20 | === 支付gas 21 | 22 | 虽然gas有价格,但它不能“拥有”也不能“花”。gas仅存在于以太坊虚拟机(EVM)内部,作为计算工作量的计数。发起方被收取ether交易费,然后转换为gas,然后转回到ether,作为矿工的块奖励。这些转换步骤用于将计算的价格(与“工作量”相关)与ether的价格(与市场波动相关)分开。 23 | 24 | === gas成本与gas价格 25 | 26 | 虽然**gas成本**是EVM中执行的操作步骤的度量,但gas本身也具有以ether测量的**gas价格**。在执行交易时,发起方指定他们愿意为每个gas单位支付的gas价格(以ether为单位),允许市场决定ether的价格与计算操作的成本之间的关系(以gas衡量) 。 27 | 28 | `total gas used * gas price paid = transaction fee`,以ether为单位 29 | 30 | 此金额将在交易执行开始时从发起方的帐户中扣除。发起方不是设置“total gas used”,而是设置**gas limit**,该限制应足以覆盖执行交易所需的gas量。 31 | 32 | === gas成本限制和gas耗尽 33 | 34 | 在发送交易之前,发起方必须指定**gas limit** - 他们愿意购买的最大gas数量。他们还必须指定**gas price** - 他们愿意为每单位gas支付的以太价格。 35 | 36 | 以ether计算的`gas limit * gas price`在交易执行开始时从发起方的账户中扣除作为存款。这是为了防止发送者在执行中期“破产”并且无法支付gas费用。由于这个原因,用户也无法设置超出其帐户余额的gas限制。 37 | 38 | 理想情况下,发起方将设置一个高于或等于实际使用的gas的gas限制。如果gas限制设置高于消耗的gas量,发货人将收到超额金额的退款,因为矿工只获得他们实际工作的补偿。 39 | 40 | 在这种情况下: 41 | 42 | `(gas限制 - 多余gas)*gas价格以太以矿块作为块奖励 43 | 44 | `(gas limit - excess gas) * gas price` ether作为矿工的区块奖励 45 | 46 | `excess gas * gas price` ether退回发起方 47 | 48 | 但是,如果使用的gas超过规定的gas限制,即如果在执行期间交易“runs out of gas”,则操作终止。虽然交易不成功,但由于矿工已经完成了计算工作,不会退回发送人的交易费用,矿工因此得到补偿。 49 | 50 | ==== 示例 51 | 52 | 如果交易是从外部拥有账户(EOA)发送的,则从EOA的余额中扣除gas费。换句话说,交易的发起人正在支付gas费。发起人为交易消耗的总gas以及随后的任何子执行提供资金。这意味着如果发起者X附加1000个gas来调用合约A,其在计算上花费500个gas然后向合约B发送另一个消息,则A用于将消息发送到B的gas也会再已开始从X的gas限制中扣除。 53 | 54 | ``` 55 | EOA帐户X启动交易并调用合约帐户A上的功能,附带1000个gas 56 | 57 | 合约A在计算上花费500gas,并向合约B发送消耗100gas的消息 58 | 59 | 合约B在计算上花费300个gas并完成交易。 60 | 61 | 100个gas退还给X. 62 | ``` 63 | 64 | 因此,如果该交易的发起人在开始时没有附加足够高的gas费,那么在交易中执行一部分操作的中间合约(例如,在我们的示例中为合约A)理论上可以耗尽gas。如果合约在执行中期用完,除了gas费支付外,所有状态变更都会被撤销。 65 | 66 | === 估算 Gas 67 | 68 | 通过假装交易已经被包含在区块链中来估算gas,然后返回如果操作是真实的那么可以收取的确切gas量。换句话说,它使用与矿工用来计算实际费用但从未开采过区块链的完全相同的程序。 69 | 70 | 请注意,由于各种原因(包括EVM机制和节点性能),估计值可能远远超过交易实际使用的gas量。 71 | 72 | ``` javascript 73 | var result = web3.eth.estimateGas({ 74 | to: "0xc4abd0339eb8d57087278718986382264244252f", 75 | data: "0xc6888fa10000000000000000000000000000000000000000000000000000000000000003" 76 | }); 77 | console.log(result); // "0x0000000000000000000000000000000000000000000000000000000000000015" 78 | ``` 79 | 80 | === gas价格和交易优先顺序 81 | 82 | gas价格是交易发起方愿意为每个gas单位支付的ether量。开采下一个区块的矿工决定要包括哪些交易。由于gas价格被计入他们将作为奖励的交易费中,他们更可能首先包括具有最高gas价格的交易。如果发起方将gas价格设置得太低,他们可能需要等待很长时间才能将其交易进入一个区块。 83 | 84 | 矿工还可以决定块中包含交易的顺序。由于多个矿工竞争将其区块附加到区块链,因此区块内的交易顺序由“获胜”矿工任意决定,然后其他矿工用该订单核实。虽然可以任意排列来自不同账户的交易,但是来自个人账户的交易是按照自动递增的随机数的顺序执行的。 85 | 86 | === 区块gas限制 87 | 88 | 区块gas限制是区块中允许的最大gas量,用于确定区块中可以容纳的交易数量。例如,假设我们有5个交易,其中每个交易的gas限制为10,20,30,40和50.如果区块gas限制为100,那么前四个交易可以适合该区块,而交易5必须等待未来的区块。如前所述,矿工决定在一个区块中包含哪些交易。不同的矿工可以尝试包括块中的最后2个交易(50 + 40),并且它们仅具有包括第一个交易(10)的空间。如果你尝试包含的交易需要的gas量超过当前的gas限制,则网络将拒绝该交易,你的以太坊客户将向你发送消息“交易超过区块gas限制。根据https://etherscan.io的数据,目前区块的gas限制在500万左右。即一个区块可以容纳约238个交易,每个交易消耗21000个gas。 89 | 90 | === 谁来决定区块gas限制是多少? 91 | 92 | 网络上的矿工决定区块gas限制是什么。想要在以太坊网络上挖矿的个人使用挖矿程序,例如ethminer,它连接到Geth或Parity Ethereum客户端。以太坊协议有一个内置机制,矿工可以对gas限制进行投票,因此无需在硬分叉上进行协调就可以增加容量。块的矿工能够在任一方向上将块气限制调整1/1024(0.0976%)。其结果是根据当时网络的需要调整块大小。这一机制与默认的开采策略结合在一起,矿工默认将投票决定至少470万的gas限制,但如果这一数字更高的话,将把目标对准最近的(1024区块指数移动)平均gas的150%,从而使数量有机地增加。矿工们可以选择改变这一点,但是他们中的许多人不这样做,并保留默认值。 93 | 94 | === gas退款 95 | 96 | 以太坊通过退还高达一半的gas费用来鼓励删除存储的变量。 97 | EVM中有2个负的gas操作: 98 | 99 | 清理合约是-24,000(SELFDESTRUCT) 100 | 清理存储为-15,000(SSTORE [x] = 0) 101 | 102 | ==== GasToken 103 | 104 | GasToken是一种符合ERC20标准的token,允许任何人在gas价格低时“储存”gas,并在gas价格高时使用gas。通过使其成为可交易的资产,它基本上创造了一个gas市场。 105 | 它的工作原理是利用前面描述的gas退款机制。 106 | 107 | 你可以在https://gastoken.io/了解计算盈利能力以及如何使用释放gas所涉及的数学 108 | 109 | === 租金 110 | 目前,以太坊社区提出了一项关于向智能合约收取“租金”以保持活力的建议。 111 | 112 | 在没有支付租金的情况下,智能合约将被“睡眠”,即使是简单的读取操作也无法获得数据。需要通过支付租金和提交Merkle证据来唤醒进入睡眠状态的合约。 113 | 114 | https://github.com/ethereum/EIPs/issues/35 115 | https://ethresear.ch/t/a-simple-and-principled-way-to-compute-rent-fees/1455 116 | https://ethresear.ch/t/improving-the-ux-of-rent-with-a-sleeping-waking-mechanism/1480 117 | 118 | <<第十四章#,下一章:以太坊虚拟机>> 119 | 120 | image::images/thanks.jpeg["赞赏译者",height=400,align="center"] 121 | 122 | -------------------------------------------------------------------------------- /第十九章.asciidoc: -------------------------------------------------------------------------------- 1 | <<第十八章#,上一章:以太坊标准>> 2 | 3 | [[ethereum_fork_history]] 4 | == 以太坊分叉历史 5 | 大多数硬分叉计划作为路线图的一部分,并包含社区普遍认同的更新; 这通常被称为共识。然而,一些硬分叉并不总是保持共识,这导致多个不同的区块链。导致以太坊/以太坊经典分裂的事件就是这种情况。 6 | 7 | [[etc_origin]] 8 | === 以太坊经典(ETC) 9 | 在以太坊社区的成员继续使用时间敏感的硬分叉(“DAO Hard Fork”)之后,以太坊经典就出现了。2016年7月20日,在以192万的区块高度上,以太坊通过硬分叉引入了不规则的状态变化,从而退还大约360万的ether,这些以太币来自一个名为The DAO的智能合约。 10 | 11 | 社区中的一些人不同意这种变化,认为这违反了以太坊的不变性; 他们选择在以太坊经典的绰号下继续保持原有的链条。虽然分裂本身最初是意识形态的,但两个链条已经发展成为它们各自独立的实体。 12 | 13 | [[dao_origin]] 14 | === 去中心化的自治组织(The DAO) 15 | 16 | DAO是由Slock.It创建的; 一支技术精湛的开发团队,包括以前的以太坊创始成员。Slock.It 将DAO视为基于社区为项目提供的资金的一种方式。其核心思想是提交提案,管理者人将管理提案,资金将从以太坊社区的投资者筹集,如果项目证明成功,那么投资者将获得一部分利润。 17 | 18 | DAO也是以太坊 token中的第一个实验之一。参与者不是直接用Ether资助项目,而是将他们的以太币交换为DAO代币,使用它们对项目资金进行投票,然后能够将它们交换为以太币。 19 | 20 | DAO token能够在2016年4月5日至4月30日期间的众筹中购买,占据了当时总价值约1.5亿美元的全部以太存款的近14%[1]。 21 | 22 | [[dao_reentrancy_bug]] 23 | === 重入( Re-Entrancy )Bug 24 | 25 | 6月9日,开发商Peter Vessenes和Chriseth报告称,大多数基于以太坊的合约管理基金都可能容易受到可以清空合约资金的漏洞<<[2]>>的影响。几天后(6月12日)斯蒂芬·塔尔(Slock.It的创始人)报告说,DAO的代码并不容易受到彼得和克里斯描述的错误的影响。<<[3]>> 令人担忧的DAO贡献者暂时松了一口气,直到5天后(6月17日),一名未知的攻击者(“DAO攻击者”)开始使用类似于6月9日描述的漏洞利用DAO <<[4]>> 。最终,DAO攻击者从DAO中吸取了大约360万个ether。 26 | 27 | 同时,一群自称为Robinhood Group(RHG)的志愿者开始使用相同的漏洞来提取剩余的资金。6月21日,RHG宣布<<[5]>>他们已经获得了另外70%的DAO,约720万以太,并计划将其退还社区。RHG的快速行动和思考被给予了很多感谢和赞扬,这有助于保护社区的大部分ether。 28 | 29 | 30 | [[dao_reentrancy_bug_technicals]] 31 | ==== Re-Entrancy技术 32 | 虽然Phil Daian <<[6]>> 描述了对该错误的更详细和详尽的解释,但简短的解释是可以在以太坊虚拟机上同时多次调用合约函数)。这允许DAO攻击者反复请求提取ether,并且在合约记录DAO攻击者已经提取之前,攻击者再次提取。 33 | 34 | [[dao_reentrancy_bug_attack_flow]] 35 | ==== Re-Entrancy 攻击流程 36 | 想象一下,你的银行账户中有100美元,你可以向你的银行出纳员提取任意数量的提款单。银行出纳员按顺序为你提供每张单据的金额,并且只有在所有单据结束时才会记录你的提款。如果你给他们带来三张单,每张100美元怎么办?如果你给他们带来三千个怎么办? 37 | 38 | *换句话说,流程是:* 39 | 40 | 1. DAO攻击者要求DAO合约提取DAO tokens。 41 | 2. 在合约更新其DAO被提取的记录之前,DAO攻击者要求合约再次提取DAO。 42 | 3. 尽可能重复第二步。 43 | 4. 合约最终记录了一次DAO的提取,失去了在此期间发生的取款。 44 | 45 | [[dao_hard_fork]] 46 | === DAO硬分叉 47 | 48 | DAO中的一想安全措施是所有提款请求都要延迟28天。这为社区提供了一个简短的时间来讨论如何处理漏洞利用。从大约6月17日到7月20日,DAO攻击者将无法将他们的DAO token转换为ether。 49 | 50 | 一些开发人员专注于寻找可行的解决方案,并在这么短的时间内探索了多种途径。其中包括6月24日宣布的DAO软分叉推迟DAO的退出,直到达成共识<<[7]>>,以及7月15日宣布的DAO硬分叉,以不正常的方式扭转DAO攻击影响的状态改变<<[8]>>。 51 | 52 | 6月28日,开发人员在DAO软分叉<<[9]>>中发现了一个DoS漏洞,并得出结论,DAO硬分叉将是fork之路上的唯一可行选择。DAO硬分叉将把所有投资于DAO的ether转移到新的退款智能合约中,允许ether的原始所有者要求全额退款。这为返还被黑的资金提供了解决方案,但也意味着干扰网络上特定地址的余额;但他们是孤立的。在DAO的部分中也会有一些剩余的ether,称为childDAO <<[12]>>。一组受托人将手动授权剩余的ether;当时的价值约为6-7百万美元<<[8]>>。 53 | 54 | 随着时间的推移,多个以太坊开发团队创建了允许用户决定是否要启用此分叉的客户端。但是,客户端创建者想要决定是否选择 opt-in(默认不分叉),或选择opt-out(默认分叉)。7月15日,Carbonvote.com上的投票开始了<<[10]>>。7月16日,在块[1,894,000] <<[11]>>,它被关闭。在以太供应总票数的5.5%中,约80%的选票(约占总供应量的4.5%)投票选择opt-out。选择opt-out的投票的四分之一来自单一地址<<[12]>>。 55 | 56 | 最终决定成为选择opt-out,反对DAO硬分叉的人需要通过更改他们运行的软件中的配置选项来不分叉。 57 | 58 | 7月20日,在块1,920,000 <<[13]>> 以太坊实施了DAO硬分叉 <<[14]>>,因此创建了两个以太坊,一个支持不规则的状态变化,另一个与它相对。 59 | 60 | 当硬分叉的以太坊(现今的以太坊)获得了大部分采矿权时,许多人认为达成共识并且少数群体链将逐渐消失; 和以前的分叉一样。尽管如此,以太坊社区的相当大一部分开始支持原来的区块链,后来被称为以太坊经典。 61 | 62 | 几天之内,几个交易所开始列出以太坊(“ETH”)和以太坊经典(“ETC”)。由于硬分叉的性质,所有在分拆时持有ether的以太网用户现在都在两个区块链中都持有资金,在Poloniex于7月24日列出ETC后,ETC的市场价值很快就建立了 <<[15]>>。 63 | 64 | [[dao_hard_fork_arguments]] 65 | ==== 硬分叉的讨论 66 | 67 | 在DAO硬分叉前几周,/r/ethereum subreddit上发生了很多讨论。一些流行/关键的论点总结如下。 68 | 69 | [cols=3*, options=header] 70 | |=== 71 | |论点 72 | |原因 73 | |反对 74 | 75 | |责任/正义 76 | |如果可能的话,社区可以负责确定是否发生了盗窃并且应该纠正。有道德要求。 77 | |确定盗窃是否已经发生并且应该纠正的责任应该只由法律机构来完成。如果受影响的各方参与决策,则无法消除偏见。 78 | 79 | |DAO协议 80 | |DAO的大多数参与者无法正确评估代码,因此他们不能同意受DAO代码的约束。 81 | |DAO的条款和条件<<[23]>>的开头段落声明“...... DAO的代码控制并阐述了DAO创作的所有条款。” 82 | 83 | |区块链不变性 84 | |区块链不变性是一种社会结构,因此如果多数人同意,我们可以改变它。 85 | |区块链不变性是一种社会结构,因此强制执行不变性非常重要。 86 | 87 | |选择加入与选择退出 88 | |社区可以选择Hard Fork是选择加入还是选择退出。我们投票决定是选择退出。 89 | |历史上Hard Forks是选择加入(即比特币)而非投票是不投票。在约1天的时间内,选择退出投票仅占总供应投票的4.5%。<< [12] >> 90 | |=== 91 | 92 | [[dao_hard_fork_timeline]] 93 | === DAO硬分叉的时间线 94 | 95 | - 4月5日:Slock.It 在Dejavu Security<<[16]>>的安全审计之后创建了DAO 96 | - 4月30日:DAO众筹推出<<[17]>> 97 | - 5月27日:DAO众筹结束 98 | - 6月9日:发现了潜在的递归调用错误,并认为它会影响跟踪用户余额的许多Solidity合约<<[2]>> 99 | - 6月12日:Stephen Tual宣布DAO资金没有风险<<[3]>> 100 | - 6月17日:DAO被利用,发现的bug的一个变种(称为“重新进入的bug”)被用来开始耗尽资金; 最终攫取了约30%的资金。<<[6]>> 101 | - 6月21日:RHG宣布它已经确保了存储在DAO中的其他~70%的以太网。<<[5]>> 102 | - 6月24日:通过Geth和Parity客户通过选择加入信号宣布软叉投票。这旨在暂时扣留资金,直到社区可以更好地决定做什么。<<[7]>> 103 | - 6月28日:软叉中发现了一个漏洞,它已被废弃。<<[9]>> 104 | - 6月28日至7月15日:用户辩论是否硬分叉。大多数争论发生在/r/ethereum subreddit上。 105 | - 7月15日:DAO Hard Fork被提议撤销DAO攻击。<<[8]>> 106 | - 7月15日:对carbonvote进行投票以决定DAO Hard Fork是否选择加入(默认情况下不分叉)或选择退出(默认为fork)。<<[10]>> 107 | - 7月16日:以太供应总票数的5.5%,约80%的选票(约占总供应量的4.5%)是选择退出硬分叉。支持投票的四分之一来自一个地址。<<[11]>> <<[12]>> 108 | - 7月20日:硬分叉发生在1,920,000块。<<[13]>> <<[14]>> 109 | - 7月20日:反对DAO Hard Fork的人继续运行旧的非硬分叉客户端软件。这会导致在两个链上重放交易的问题。<<[18]>> 110 | - 7月24日:Poloniex在股票代码ETC下列出原始的以太坊链; 这是第一次交换。<<[15]>> 111 | - 8月10日:RHG将290万回收的ETC转移至Poloniex,以便在Bity SA的建议下将其转换为ETH。RHG总持有量的14%从ETC转换为ETH和其他加密货币。Poloniex冻结了另外86%的沉积ETH。<<[19]>> 112 | - 8月30日:冻结的资金由Poloniex发送回RHG。然后RHG在ETC链上设立退款合约。<<[20]>> <<[21]>> 113 | - 12月11日:IOHK的ETC开发团队组建。由以太坊创始成员Charles Hoskinson领导。 114 | - 2017年1月13日:更新ETC网络以解决交易重播问题。这两个链现在在功能上是分开的。<<[22]>> 115 | - 2月20日:ETCDEVTeam表格。早期ETC开发人员Igor Artamonov(splix)领导。 116 | 117 | [[eth_etc_differences]] 118 | === 以太坊和以太坊经典 119 | 120 | 虽然最初的分裂以DAO为中心,但以太坊和以太坊经典现在是独立的项目。完整的差异是不断发展的,而且过于广泛而无法在本章涵盖,值得注意的是,这些链条在核心发展和社区结构方面确实存在显着差异。 121 | 122 | [[eth_etc_differences_technical]] 123 | === 技术差异 124 | 125 | [[eth_etc_differences_evm]] 126 | ==== EVM 127 | 对于大多数部分(截至2018年4月),两个网络保持高度兼容。为一条链生成的合约代码在另一条链上按预期运行。尽管EVM操作系统的差异很小(参见EIPs: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-140.md[140], link:https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md[145], 和link:https://github.com/ethereum/EIPs/blob/master/EIPS/eip-214.md[214]) 128 | 129 | [[eth_etc_differences_core_development]] 130 | ==== 核心网络开发 131 | 所有区块链最终都有很多用户和贡献者。但是,由于开发此类软件所需的专业知识,核心网络开发(运行网络的代码)通常由分散的小组完成。因此,这些小组生成的代码与实际运行网络的代码密切相关。 132 | 133 | [cols=2*, options=header] 134 | |=== 135 | |Ethereum 136 | |Ethereum Classic 137 | 138 | |以太坊基金会和志愿者。 139 | |ETCDEV, IOHK, 和志愿者 140 | |=== 141 | 142 | [[eth_etc_differences_ideological]] 143 | === 意识形态差异 144 | 以太坊和以太坊经典之间最大的物质差异之一是意识形态,它以两种主要方式表现出来:不变性和社区结构。 145 | 146 | [[eth_etc_differences_immutability]] 147 | ==== 不变性 148 | 在区块链的背景下,不变性指的是区块链历史的保存。 149 | 150 | [cols=2*, options=header] 151 | |=== 152 | |Ethereum 153 | |Ethereum Classic 154 | 155 | |遵循一种俗称“治理”的哲学。这种理念允许参与者以不同程度的代表性投票,在某些情况下(例如DAO攻击)改变区块链。 156 | |遵循一种理念,即一旦数据出现在区块链上,就不能被其他人修改。这是与比特币,Litecoin和其他加密货币共享的理念。 157 | |=== 158 | 159 | [[eth_etc_differences_community_structure]] 160 | ==== 社区结构 161 | 虽然区块链旨在分散,但它们周围的世界大部分都是集中的。以太坊和以太坊经典以不同的方式处理这一现实。 162 | 163 | [cols=2*, options=header] 164 | |=== 165 | |Ethereum 166 | |Ethereum Classic 167 | 168 | |_以太坊基金会所有:/r/ethereum Subreddit, ethereum.org 网站, 论坛, GitHub (ethereum), Twitter (@ethereum), Facebook, 和 Google+ account. 169 | |_由单独的实体所有:/r/ethereumclassic Subreddit, the ethereumclassic.org 网站, 论坛, GitHubs (ethereumproject, ethereumclassic, etcdevteam, iohk, ethereumcommonwealth), Twitter (@eth_classic), Telegrams, 和 Discord. 170 | |=== 171 | 172 | [[other_ethereum_forks]] 173 | === 着名的以太坊分叉的时间表 174 | 175 | 在以太坊也发生了其他几个分叉。其中一些是硬分叉,因为它们直接从预先存在的以太坊网络中分离出来。其他是软分叉:它们使用以太坊的客户端/节点软件,但运行完全独立的网络,没有与以太坊共享的任何历史记录。在以太坊的生活中可能会有更多的分叉。 176 | 177 | 还有一些其他项目声称是以太坊分叉,但实际上是基于ERC20 token并在以太坊网络上运行。其中两个例子是EtherBTC(ETHB)和以太坊修改(EMOD)。这些不是传统意义上的分叉,有时也可称为空投。 178 | 179 | - Expanse是以太坊区块链的第一个获得牵引力的分支。它是在2015年9月7日通过比特币谈话论坛宣布的。实际的分叉发生在一周后的2015年9月14日,块高度为800,000。它最初由Christopher Franko和James Clayton创立。他们的愿景是创建一个先进的链:“身份,治理,慈善,商业和公平”。 180 | - EthereumFog(ETF)于2017年12月14日推出,分块高度为4730660。他们的目标是通过专注于雾计算和分散存储来开发“世界分散雾计算”。关于这实际上会带来什么的信息仍然很少。 181 | - EtherZero(ETZ)于2018年1月19日发布,块高4936270,块高4936270。其值得注意的创新是引入了masternode架构并取消了智能合约的交易费用,以实现更广泛的DAPP。以太网社区的一些著名成员MyEtherWallet和MetaMask遭到了一些批评,原因是围绕开发缺乏明确性以及对可能的网络钓鱼的一些指责。 182 | - EtherInc(ETI)于2018年2月13日发布,高度为5078585,重点是建立分散的组织。他们还宣布减少封锁时间,增加矿工奖励,取消叔叔奖励并设置可开采硬币的上限。它们使用与以太坊相同的私钥,并实施了重放保护,以保护原始非重制链上的ether。 183 | 184 | [bibliography] 185 | === 参考 186 | - [[[1]]] https://www.economist.com/news/finance-and-economics/21699159-new-automated-investment-fund-has-attracted-stacks-digital-money-dao 187 | 188 | - [[[2]]] http://vessenes.com/more-ethereum-attacks-race-to-empty-is-the-real-deal/ 189 | 190 | - [[[3]]] https://blog.slock.it/no-dao-funds-at-risk-following-the-ethereum-smart-contract-recursive-call-bug-discovery-29f482d348b 191 | 192 | - [[[4]]] http://hackingdistributed.com/2016/06/18/analysis-of-the-dao-exploit 193 | 194 | - [[[5]]] https://www.reddit.com/r/ethereum/comments/4p7mhc/update_on_the_white_hat_attack/ 195 | 196 | - [[[6]]] http://hackingdistributed.com/2016/06/18/analysis-of-the-dao-exploit/ 197 | 198 | - [[[7]]] https://blog.ethereum.org/2016/06/24/dao-wars-youre-voice-soft-fork-dilemma/ 199 | 200 | - [[[8]]] https://blog.slock.it/hard-fork-specification-24b889e70703 201 | 202 | - [[[9]]] https://blog.ethereum.org/2016/06/28/security-alert-dos-vulnerability-in-the-soft-fork/ 203 | 204 | - [[[10]]] https://blog.ethereum.org/2016/07/15/to-fork-or-not-to-fork/ 205 | 206 | - [[[11]]] https://etherscan.io/block/1894000 207 | 208 | - [[[12]]] https://elaineou.com/2016/07/18/stick-a-fork-in-ethereum/ 209 | 210 | - [[[13]]] https://etherscan.io/block/1920000 211 | 212 | - [[[14]]] https://blog.ethereum.org/2016/07/20/hard-fork-completed/ 213 | 214 | - [[[15]]] https://twitter.com/poloniex/status/757068619234803712 215 | 216 | - [[[16]]] https://blog.slock.it/deja-vu-dao-smart-contracts-audit-results-d26bc088e32e 217 | 218 | - [[[17]]] https://blog.slock.it/the-dao-creation-is-now-live-2270fd23affc 219 | 220 | - [[[18]]] https://gastracker.io/block/0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f 221 | 222 | - [[[19]]] https://bitcoinmagazine.com/articles/millions-of-dollars-worth-of-etc-may-soon-be-dumped-on-the-market-1472567361/ 223 | 224 | - [[[20]]] https://medium.com/@jackfru1t/the-robin-hood-group-and-etc-bdc6a0c111c3 225 | 226 | - [[[21]]] https://www.reddit.com/r/EthereumClassic/comments/4xauca/follow_up_statement_on_the_etc_salvaged_from/ 227 | 228 | - [[[22]]] https://www.reddit.com/r/EthereumClassic/comments/5nt4qm/diehard_etc_protocol_upgrade_successful_nethash/ 229 | 230 | - [[[23]]] https://web.archive.org/web/20160429141714/https://daohub.org/explainer.html/ 231 | 232 | - [[[24]]] https://ethereumclassic.github.io/blog/2016-12-12-TeamGrothendieck/ 233 | 234 | 全书完结 235 | 236 | image::images/thanks.jpeg["赞赏译者",height=400,align="center"] 237 | -------------------------------------------------------------------------------- /第十五章.asciidoc: -------------------------------------------------------------------------------- 1 | <<第十四章#,上一章:以太坊虚拟机>> 2 | 3 | [Consensus] 4 | 5 | == 共识 6 | 7 | 以太坊网络中的共识是指多个节点或代理在给定的时间点就区块链状态达成一致的能力。这与传统的定义为个人或群体之间的一般协议的共识密切相关但不同。在这里,社区必须解决在技术上(在网络内)和社交上达成共识的挑战(以确保协议不会分叉或破裂)。本章将概述建立共识的一些技术方法。 8 | 9 | 当涉及区块链上分散记录保存和验证的核心功能时,单独依靠信任来确保添加到账本的信息是正确的可能会成为问题。这种挑战在去中心化网络中更为明显,因为没有中央实体来决定什么应该和不应该被视为是真实的。缺乏一个中央决策实体是区块链受欢迎程度的主要吸引力之一,因为系统能够抵抗审查制度,并且无需对许可或信息获取权限的依赖。然而,这些好处可能带来成本,因为如果没有可信的仲裁员,任何分歧,欺骗或差异都需要使用数学,经济或社会技术进行协调。因此,分散的系统更有抵御攻击的能力,但在应对变化时却不那么果断。 10 | 11 | 获得共识和信任信息的能力将对区块链技术作为资产类别和技术的未来采用和实用具有重要意义。为了应对这一挑战并保持权力下放的重要性,社区不断尝试不同共识模式,我们将在本章中探讨。 12 | 13 | === 共识度量 14 | 15 | 共识度量是可测量的数据,区块链网络的节点必须在该数据上达成一致,以便为每个块中包含的数据建立并保持一致。在区块链技术中,每次将新块添加到链中时,每个网络节点都会测量并批准一致性度量。作为共识度量的结果,区块链充当了从一个确定可验证的事实延伸到下一个事实的真理链。由于共识度量,区块链协议的节点变为 _迷你公证人_ _mini-notaries_,能够从真实的节点中立即分辨出区块链的错误副本,并将该事实报告给整个网络。这些措施是必需的,以便阻止通过提交包含虚假信息的区块来欺骗网络不良行为者。由于一致性度量,区块链不仅建立了的完整性,而且长期保持不变。共识度量有多种形式,但对于此讨论而言,最重要的两种是基于风险的度量和基于工作量的度量。 16 | 17 | === 基于Hash的度量 18 | 19 | 通常称为工作量证明(PoW)度量,这些度量建立了共识,因为使用它们的协议将计算机设置为查找难题的答案。找到适合网络参数的散列的难题要求节点提交处理能力并使用电力与其他节点竞争以提出有效的哈希。为了便于说明,可以考虑超级计算机,它的唯一工作就是在整数空间中搜索素数。现在考虑由普通计算机组成的整个网络。这些计算机放在一起时,可以说具有超级计算机的组合计算能力。这个计算机网络的唯一工作类似于搜索另一种称为SHA-256哈希的数字的可能数字。这些数字具有独特的属性,就像素数一样,尽管在生成符合网络设定标准的哈希方面存在很大困难,但在发现时可以轻松验证它们。想象计算哈希和验证哈希的一种方法是使用拼图游戏类比。这个难题非常困难且耗时,但是一眼就能看出它是否已经完成。 20 | 21 | 当准确计算SHA-256哈希值时,它们可用作证明已使用一定量的计算能力来查找数字的证明。最新的素数是asciimath:[2^(77,232,917)- 1]。它是由计算机发现的,就像计算机发现的SHA-256哈希一样。SHA-256哈希比新素数更容易找到,但是找到哈希的固有难点在于基于哈希的度量得出它们的能力。 22 | 23 | 每个SHA-256哈希都有64个十六进制字符。例如,这里是单词“yank”的SHA-256哈希。 24 | `“yank”(SHA-256)= 45F1B9FC8FD5F760A2134289579DF920AA55830F2A23DCF50D34E16C1292D7E0` 25 | 26 | 将其与三个字母“yan”的SHA-256哈希: 27 | 28 | `“yan”(SHA-256)= 281ACA1A80B52620BD717E8B14A0386B8ADA92AE859AC2C8A2AC222EFA0​​2EDBB` 29 | 30 | 两个字母“ya”的SHA-256哈希: 31 | 32 | `“ya”(SHA-256)= 6663103A3E47EFC879EA31FA38458BE23BE0CE0895F3D8B27B7EA19A1120B3D4` 33 | 34 | 单个字母“y”的SHA-256哈希: 35 | 36 | `“y”(SHA-256)= A1FCE4363854FF888CFF4B8E7875D600C2682390412A8CF79B37D0B11148B0FA` 37 | 38 | === 基于哈希的度量验证 39 | 40 | 如果你对足够多的随机短语进行了哈希,有点像打字机上的猴子,最终你会发现一个匹配特定模式的哈希。在哈希为“ya”的情况下,请注意它以模式“666”开头。这类似于比特币,但比特币要求找到与以“000”开头的模式匹配的哈希值。可以通过将前一个区块中的信息插入到SHA-256哈希算法中来创建的任何哈希,并用于创建下一个块,只要它在数字中具有正确数量的前导零,网络就会认可,并且区块奖励将是你的。 41 | 42 | 由于想要挖掘比特币的人数不断增加,因此每秒寻找SHA-256哈希值的算力总是越来越多。比特币软件通过自动调整共识度量的难度来处理这种意外事件,增加前导零的数量以形成共识。这可以保证新块的创建时间与前面的块大致相同。对于比特币网络,为十分钟,但可以轻松更改。在以太坊中,平均区块生成时间约为10秒。 43 | 44 | == 基于风险的度量 45 | 46 | 通常称为Proof-of-Stake(PoS)度量,这些度量基于以下事实建立共识:选择创建无效区块的每个人都会失去比通过创建有效区块获得的更多的东西。该度量是通过关于链内数据的共识创造的,而不是关于链外数据的共识。基于哈希的共识度量主要关注SHA-256哈希的质量和精确性。基于风险的度量主要关注任何特定节点在添加新块时所承担的风险。 47 | 48 | 所有节点在这里达成一致的度量是哪些节点创建了正确的块,哪些节点没有。在某种程度上,这已经内置在比特币协议中。比特币协议假定正确的块是大多数节点正在挖掘的块。它假设矿工不会选择不正确的区块,因为这不利于挖掘坏区块。 49 | 50 | 另一方面,基于风险的链依赖于对未能创建网络中其他节点认可的块的创建者的快速,即时和不可逆转的影响。通过强制执行资源丢失的风险,基于哈希的度量(也依赖于不想浪费资源的人员)过程可以以更简单的方式进行缩短和实施。目前正在研究在以太坊中实现这种共识度量模型。 51 | 52 | 工作量证明是一种共识协议,它将网络中的有效区块链视为创建计算成本最高的链。这里提到的计算工作是将所有块添加到当前区块链所必须完成的工作。这项工作由网络节点完成,并且必须在计算上很困难,以便使工作变得非常重要,但也必须是可行的,以便在经过合理的努力之后可以实现。最终,网络将依赖于提供此PoW的节点以维持区块链,因此,网络的最佳利益是需要合理的PoW。 53 | 54 | 在以太坊网络以及许多其他区块链网络中,获取PoW需要找到要添加到区块链的块的哈希。这个哈希是通过散列由块的数据和随机数组成的字符串获得的(创建此字符串的方法可能会有所不同,但整个过程是相同的)。该哈希必须小于某个阈值(由网络的难度确定),并且一旦节点发现产生该哈希的随机数,则接受相应的块并将其添加到区块链中。 55 | 56 | 找到这个有效散列的方法是修改nonce,通常将其初始化为零并在每次迭代时递增,直到产生低于网络阈值的哈希。此过程称为挖掘。由于挖掘中使用的哈希函数的性质,找到有效随机数的唯一方法是通过暴力搜索,即检查随机数的每个可能值,直到找到满足网络要求的散列。因此,提供有效的随机数被认为是PoW。 57 | 58 | === PoS 59 | 60 | 权益证明(PoS,Proof-of-Stake)是公共区块链的一类共识算法,它依赖于验证者在网络中的经济利益。在基于工作量证明(PoW)的公共区块链(例如比特币和以太坊的当前实现)中,该算法奖励解密加密谜题的参与者,以便验证交易并创建新的块(即挖掘)。在基于PoS的公共区块链中(例如以太坊即将发布的Casper实现),一组验证者轮流对下一个块进行建议和投票,每个验证者的投票权重取决于其存款的大小(即赌注)。PoS的显着优势包括安全性,降低集中风险和能源效率。 61 | 62 | 通常,权益证明算法如下。区块链跟踪一组验证者,任何持有区块链基本加密货币的人(在以太坊的情况下是ether)都可以通过发送一种特殊类型的交易来将其以太币锁定到存款中,从而成为验证者。然后,通过所有当前验证者都可以参与的一致性算法来完成创建和同意新块的过程。 63 | 64 | 有许多种共识算法,以及许多方法可以为参与共识算法的验证人分配奖励,因此有许多“口味”的权益证明。从算法的角度来看,有两种主要类型:基于链的权益证明和BFT风格的权益证明。 65 | 66 | * 在基于链的证明中,算法在每个时隙中伪随机地选择一个验证者(例如,每个10秒的周期可能是一个时隙),并为该验证者分配创建单个块的权限,这个块必须指向一些前一个块(通常是前一个最长链末端的块),因此随着时间的推移,大多数块会聚成一个不断增长的链。 67 | 68 | * 在BFT风格的股权证明中,验证者被随机分配提出区块的权利,但是通过多轮过程来确定哪个区块是规范的,其中每个验证者在每轮中发送对某个特定区块的“投票”,在流程结束时,所有(诚实和在线)验证者永久同意任何给定的块是否属于链条的一部分。请注意,块可能仍然链接在一起; 关键的区别在于块上的共识可以在一个块内,并且不依赖于它之后的链的长度或大小。 69 | 70 | ==== PoA 71 | 72 | 授权证明(PoA)是PoS一致性算法的子集,主要由测试网和私有或联盟网络使用。在基于PoA的区块链中,交易有效性最终由一组经批准的链上账户确定,称为“授权节点”。确定授权节点的标准是通过网络治理结构中编写的方法确定性地决定的。 73 | 74 | PoA被广泛认为是达成共识的最快途径,但依赖于验证节点尚未受到损害的假设。非验证参与者可以像公共以太网那样访问和使用网络(通过利用p2p交易,合约,账户等) 75 | 76 | PoA共识依赖于验证者的声誉和过去的表现。这个想法是验证者节点将其身份/声誉放到我的身上。私人联盟网络的一个重要方面是链上地址与已知的现实世界身份之间的联系。因此,我们可以说验证节点正在盯着他们的“身份”或“声誉”(而不是他们的经济持有)。这为验证者创建了一定程度的问责制,最适合企业,私有或测试网络。 77 | 78 | PoA目前由测试网络Kovan(PoA网络)使用,并且可以在Parity中轻松配置用于私人联盟网络。 79 | 80 | ==== DPoS 81 | 82 | 代理权益证明(DPoS)是一种经过修改的权益证明形式,网络参与者投票选举一系列代表(也称为证人)来验证和保护区块链。这些代表有点类似于PoA中的权威节点,除非他们的权限可能被选民撤销。 83 | 84 | 在DPoS共识中,与PoS一样,投票权重与用户注入的投注金额成正比。这就产生了一个场景,即较多token持有者比较少token的持有者拥有更多的投票权。从游戏理论的角度来看,这是有道理的,因为那些具有更多经济的“游戏中的皮肤”的人自然会有更大的动力来选出最有效的代表证人。 85 | 86 | 此外,代表证人会收到验证每个区块的奖励,因此被激励保持诚实和有效 - 以免被替换。然而,有一些方法可以使“贿赂”变得相当合理; 例如,交易所可以提供存款利率(或者更加含糊地,使用交易所自己的资金建立一个很好的界面和功能),交易所运营商可以使用大量存款进行DPoS共识投票。。 87 | 88 | == 以太坊的共识 89 | 90 | === Ethash简介 91 | 92 | Ethash是以太坊*工作量证明(PoW)算法*,它依赖于数据集的初始纪元的生成,该数据集的大小约为1GB,称为有向无环图(*DAG*)。*DAG*使用* Dagger-Hashimoto算法*的版本,它是*Vitalik Buterin的Dagger算法*和*Thaddeus Dryja的Hashimoto算法*的组合。* Dagger-Hashimoto算法*是以太坊1.0使用的挖掘算法。随着时间的推移,*DAG*线性增长,每*纪元*(30,000块,125小时)更新一次。 93 | 94 | ==== 种子,缓存,数据生成 95 | 96 | *PoW算法*涉及: + 97 | - 通过扫描*DAG*的先前块头来计算每个块的*Seed*。+ 98 | - *Cache* 是一个16MB的伪随机缓存,根据种子计算,用于轻量级客户端中的存储。 + 99 | - 来自cache的*DAG* *Data Generation* 在完整客户端和矿工上用于存储 (数据集中的每一项只依赖cache中的一小部分项目)+ 100 | - *矿工*通过随机抽取数据集的片段并将它们混合在一起进行挖掘。可以使用存储的缓存和低内存进行验证,以重新生成所需的数据集的特定部分。 101 | 102 | .参考: 103 | - Ethash-DAG: https://github.com/ethereum/wiki/wiki/Ethash-DAG 104 | - Ethash Specification: https://github.com/ethereum/wiki/wiki/Ethash 105 | - Mining Ethash DAG: https://github.com/ethereum/wiki/wiki/Mining#ethash-dag 106 | - Dagger-Hashimoto Algorithm: https://github.com/ethereum/wiki/blob/master/Dagger-Hashimoto.md 107 | - DAG Explanation and Images: https://ethereum.stackexchange.com/questions/1993/what-actually-is-a-dag 108 | - Ethash in Ethereum Yellowpaper: https://ethereum.github.io/yellowpaper/paper.pdf#appendix.J 109 | - Ethash C API Example Usage: https://github.com/ethereum/wiki/wiki/Ethash-C-API 110 | 111 | === Polkadot简介 112 | 113 | Polkadot是一种链间区块链协议,包括与权益证明(PoS)链的整合,允许Parachain在没有内部共识的情况下获得共识。 114 | 115 | .Polkadot包括: 116 | 117 | - *Relay-Chains* 连接到所有Parachains并协调区块链之间的共识和交易传递,并使用*验证函数*通过验证PoV候选块的正确性来促进Parachain交易的最终确定。 118 | - *Parachains*(跨网络的并行链),它们是区块链,用于收集和并行处理交易以实现可伸缩性。 119 | - 无需信任,交易直接在区块链之间转移,而不是通过中间人或分散交易所。 120 | - *汇总安全*,根据共识协议规则(*Rules*)检查Parachain交易有效性。通过结合由动态治理系统确定的每个集团成员的一定比例的权益token资本来实现安全性。群组成员资格需要绑定来自Validators和Nominators的赌注token的输入,如果出现不良行为,可以在试验中使用不当行为证明进行扣除。 121 | - *Bridges* 通过解耦具有不同共识架构机制的区块链网络之间的链接来提供可扩展性。 122 | - *Collators* 负责监管和维护特定的Parachain,方法是将其可用交易整理为有效性证明(PoV)候选块,向Validators报告以证明交易有效并在块中正确执行。如果它有winning ticket(由最接近Golden Ticket的Polkadot地址的Collator签名)并且变得规范和最终确定,则通过支付他们从创建PoV候选区块所收集的任何交易费来激励他们。Collators被给予Polkadot地址。胶合剂不与铆接标记粘合。 123 | - *Golden Ticket*是包含奖励的每个Parachain的每个区块中的特定Polkadot地址。Collators被赋予一个Polkadot地址,并向Validator提供由Collator签名的PoV候选块。奖励的获奖者在PoV候选区块中有一个Collator Polkadot地址,该区域最接近Golden Ticket Polkadot地址 124 | - *Fisherman* 监控Polkadot网络交易,以发现Polkadot社区的不良行为。将验证者带到法庭并证明他们表现得很糟糕的Fisherman会被确认者的债券激励,因为债券被用作惩罚不良行为的惩罚。 125 | - *验证者* 是Parachain社区中的维护者,他们被部署到不同的Parachains来监管系统。验证者同意Merkle Trees的根源。验证者必须使交易可用。渔民可以将验证员带到法庭,因为没有进行交易,相关的Collat​​ors可能会质疑该交易是否可以作为整理证明。 126 | - *提名者*(类似于PoW挖掘)被动监督并投票给他们认为可以通过赌注代币资助他们认可的确认者。 127 | 128 | Polkadot的Relay-Chains使用*Proof of Stake(PoS*系统,其中结构化状态机(SM)并行执行多个拜占庭容错(BFT)共识,以便SM过程收敛于越多个Parachain维度的包含有效候选者的解决方案跨的块。每个Parachain中的有效候选块是根据交易的可用性和有效性确定的,因为根据共识机制,目标验证者(下一个块)只有在具有足够的交易信息时才能从源验证者(前一个块)执行传入消息。可用和有效。验证人投票选择Collators使用规则达成共识的有效候选区块。 129 | 130 | .参考 131 | - Polkadot link: https://polkadot.network 132 | - Polkadot presentation at Berlin Parity Ethereum link: https://www.youtube.com/watch?v=gbXEcNTgNco 133 | 134 | <<第十六章#,下一章:Vyper:面向合约的编程语言>> 135 | 136 | image::images/thanks.jpeg["赞赏译者",height=400,align="center"] 137 | -------------------------------------------------------------------------------- /第十六章.asciidoc: -------------------------------------------------------------------------------- 1 | <<第十五章#,上一章:共识>> 2 | 3 | [Vyper] 4 | [[viper_chap]] 5 | == Vyper: 面向合约的编程语言 6 | 7 | 研究表明,具有跟踪漏洞的智能合约可能导致意外执行。https://arxiv.org/pdf/1802.06038.pdf[最近的一项研究] 分析了970,898份合约。它概述了跟踪漏洞的三个基本类别(已经导致以太坊用户的灾难性资金损失)。这些类别包括:: 8 | * 自杀合约。可以被任意地址杀死的合约 9 | * 贪婪的合约,在某个执行状态后无法释放ether 10 | * 浪费合约,不经意地将ether释放到任意地址 11 | 12 | Vyper是一种面向合约的实验性编程语言,面向以太坊虚拟机(EVM)。Vyper致力于通过简化代码并使其对人类可读而提供卓越的审计能力。Vyper的一个原则是让开发人员几乎不可能编写误导性代码。这可以通过多种方式完成,我们将在下面介绍。 13 | 14 | [[comparison_to_solidity_sec]] 15 | === 与 Solidity 比较 16 | 17 | 本节是那些正在考虑使用Vyper编程语言开发智能合约的人的参考。该部分主要对比了Vyper与Solidity的对比; 概览,合理的推理,为什么Vyper不包括以下传统的面向对象编程(OOP)概念: 18 | 19 | Modifiers:: 在Solidity中,你可以使用修饰器编写函数。例如,以下函数`changeOwner`将在一个名为`onlyBy`的修饰器中运行代码,作为其执行的一部分。 20 | 21 | [source,javascript] 22 | ---- 23 | function changeOwner(address _newOwner) 24 | public 25 | onlyBy(owner) 26 | { 27 | owner = _newOwner; 28 | } 29 | ---- 30 | 31 | 正如我们在下面看到的,名为`onlyBy`的修饰器强制执行与所有权相关的规则。虽然修饰器很强大(能够改变函数体中发生的事情),但它们也可能导致误导性的代码执行。例如,确保`changeOwner`函数逻辑的唯一方法是每次实现代码时检查并测试`onlyBy`修饰器。显然,如果将来更改修改器,则调用它的函数可能会产生与最初预期不同的结果。 32 | 33 | [source,javascript] 34 | ---- 35 | modifier onlyBy(address _account) 36 | { 37 | require(msg.sender == _account); 38 | _; 39 | } 40 | ---- 41 | 42 | 总的来说,修饰器的通常用例是在函数执行之前执行单个检查。鉴于这种情况,Vyper的建议是完全取消修饰器,并简单地使用内联检查和断言作为函数的一部分。这样做将提高审计能力和可读性,因为Vyper函数将在明显的视线中遵循逻辑内联序列,而不必引用已在别处编写的修饰器代码。 43 | 44 | 类继承:: 继承允许程序员通过从现有软件库中获取预先存在的功能,属性和行为来利用预先编写的代码。继承功能强大,可以促进代码的重用。Solidity支持多重继承以及多态,虽然这些被认为是面向对象编程的一些最重要的特性,但Vyper并不支持它们。Vyper坚持认为继承的实现要求编码人员和审计人员在多个文件之间跳转,以便了解程序正在做什么。Vyper还了解优先级规则以及多个继承如何使代码过于复杂而无法理解。鉴于Solidity https://github.com/ethereum/solidity/blob/release/docs/contracts#inheritance[关于继承的文档]给出了多重继承为何有问题的例子,这是一个公平的陈述。 45 | 46 | 内联汇编:: 内联汇编为开发人员提供了以低级别访问以太坊虚拟机(EVM)的机会。使用内联汇编代码(在更高级别的源代码中)时,开发人员可以通过直接访问EVM操作码指令来执行操作。例如,以下内联汇编代码通过使用EVM操作码mload在内存位置0x80处添加3。 47 | 48 | [source,assembly] 49 | ---- 50 | 3 0x80 mload add 0x80 mstore 51 | ---- 52 | 53 | 如前所述,Vyper致力于为开发人员和代码审计人员提供最易读的代码。虽然内联汇编可以提供强大的细粒度控制,但Vyper编程语言不支持它。 54 | 55 | 函数重载:: 具有相同名称和不同参数选项的多个函数定义会导致在任何给定时间调用哪个函数时会产生很多混淆。随着函数重载,编写误导代码会更容易( foo(“hello”)记录“hello”但foo(“hello”,“world”)窃取你的资金。)函数重载的另一个问题是它使代码更难以搜索,因为你必须跟踪哪个调用指的是哪个功能。 56 | 57 | 变量类型转换:: 类型转换是一种允许程序员将变量从一种数据类型转换为另一种数据类型的机制。 58 | 59 | 前置条件和后置条件:: 60 | Vyper明确处理前置条件,后置条件和状态更改。虽然这会产生冗余代码,但它也允许最大的可读性和安全性。在Vyper中编写智能合约时,开发人员应遵守以下3点。理想情况下,应仔细考虑3个点中的每个点,然后在代码中进行详细记录。这样做将改进代码的设计,最终使代码更具可读性和可审计性。 61 | 62 | * 条件 - 以太坊状态变量的当前状态/条件是什么 63 | * 效果 - 这个智能合约代码对执行状态变量的条件有什么影响,即什么会影响,什么不会受到影响?这些影响是否与智能合约的意图一致? 64 | * 交互 - 现在已经详尽地处理了前两个步骤,现在是运行代码的时候了。在部署之前,逻辑上逐步执行代码并考虑执行代码的所有可能的永久结果,后果和方案,包括与其他合约的交互。 65 | 66 | [[a_new_programming_paradigm_sec]] 67 | === 一种新的编程范式 68 | 69 | Vyper的创作为新的编程范式打开了大门。例如,Vyper正在删除类继承以及其他功能,因此可以说Vyper偏离了传统的面向对象编程(OOP)范例,这很好。 70 | 71 | 历史上,OOP提供了一种表示现实世界对象的机制。例如,OOP允许实例化可以从person类继承的employee对象。然而,从价值转移和/或智能合约的角度来看,那些渴望功能性编程范式的人会同意,交易性编程绝不适合上述传统的OOP范式。简而言之,交易计算是与现实世界对象分开的世界。例如,你最后一次持有交易或正向链接业务规则的时间是什么时候? 72 | 73 | 似乎Vyper没有与OOP范例或函数式编程范例完全一致(完整的原因列表超出了本章的范围)。出于这个原因,在开发的早期阶段,我们能够如此大胆地推出新的软件开发范例吗?一个致力于未来证明区块链可执行代码的人。一个可以防止在不可改变的环境中造成灾难性资金损失的人。区块链革命中经历的过去事件有机地为这一领域的进一步研究和发展创造了新的机会。也许这种研究和开发的结果最终可能导致软件开发的新的不变性范式分类。 74 | 75 | [[decorators_sec]] 76 | === 装饰符 77 | 向 `@private` `@public` `@constant` `@payable` 这样的装饰符在每个函数的开头声明。 78 | 79 | Private:: `@private` 使合约外部的函数无法访问此函数。 80 | 81 | Public:: `@public` 使该函数公开可见和可执行。例如,即使是以太坊钱包也会在查看合约时显示公共函数。 82 | 83 | Constant:: 以 `@constant` 开始的函数不允许状态变量的改变,实际上,如果函数尝试更改状态变量,编译器将拒绝整个程序(带有适当的警告)。如果该函数用于更改状态变量,则不要在函数的开头使用`@ constant`。 84 | 85 | Payable:: 只有以 `@payable` 开头声明的函数才能接收价值。 86 | 87 | Vyper明确地实现了装饰符的逻辑。例如,如果一个函数前面有一个`@appay`装饰符和一个`@ constant`装饰符,那么Vyper代码编译过程就会失败。当然,这是有道理的,因为常量函数(仅从全局状态读取的函数)永远不需要参与值的转移。此外,每个Vyper函数必须以`@ public`或`@private`装饰符开头,以避免编译失败。同时使用`@public`装饰符和`@private`装饰符的Vyper函数也会导致编译失败。 88 | 89 | [[online_code_editor_and_compiler_sec]] 90 | === 在线代码编辑器和编译器 91 | 92 | Vyper在以下URL <> 上有自己的在线代码编辑器和编译器。这个Vyper在线编译器允许你仅使用Web浏览器编写智能合约,然后将其编译为字节码,ABI和LLL。Vyper在线编译器具有各种预先编写的智能合约,以方便你使用。这些包括简单的公开拍卖,安全的远程购买,ERC20 token等。 93 | 94 | [[compiling_using_the_command_line_sec]] 95 | === 使用命令行编译 96 | 每个Vyper合约都保存在扩展名为.v.py的单个文件中。 97 | 安装完成后,Vyper可以通过运行以下命令来编译和提供字节码 98 | 99 | vyper ~/hello_world.v.py 100 | 101 | 通过运行以下命令可以获得人类可读的ABI代码(JSON格式) 102 | 103 | vyper -f json ~/hello_world.v.py 104 | 105 | [[reading_and_writing_data_sec]] 106 | === 读写数据 107 | 108 | 智能合约可以将数据写入两个地方,即以太坊的全球状态查找树或以太坊的链数据。虽然存储,读取和修改数据的成本很高,但这些存储操作是大多数智能合约的必要组成部分。 109 | 110 | 全局状态:: 给定智能合约中的状态变量存储在以太坊的全局状态查找树中,给定的智能合约只能存储,读取和修改与该合约地址相关的数据(即智能合约无法读取或写入其他智能合约)。 111 | 112 | Log:: 如前所述,智能合约也可以通过日志事件写入以太坊的链数据。虽然Vyper最初使用 pass:[__]logpass:[__] 语法来声明这些事件,但已经进行了更新,使Vyper的事件声明更符合Solidity的原始语法。例如,Vyper声明的一个名为MyLog的事件最初是 `MyLog: pass:[__]logpass:[__]({arg1: indexed(bytes[3])})`,Vyper的语法现在变为 `MyLog: event({arg1: indexed(bytes[3])})`。需要注意的是,在Vyper中执行日志事件仍然是如下 `log.MyLog("123")`。 113 | 114 | 虽然智能合约可以写入以太坊的链数据(通过日志事件),但智能合约无法读取他们创建的链上日志事件。尽管如此,通过日志事件写入以太坊的链数据的一个好处是,可以在公共链上由轻客户端发现和读取日志。例如,挖到的块中的logsBloom值可以指示是否存在日志事件。一旦建立,就可以通过日志路径获取 logs -> data inside a given transaction receipt。 115 | 116 | [[erc20_token_interface_implementation_sec]] 117 | === ERC20令牌接口实现 118 | Vyper已将ERC20实施为预编译合约,并允许默认使用它。 119 | Vyper中的合约必须声明为全局变量。声明ERC20变量的示例可以是 120 | 121 | token: address(ERC20) 122 | 123 | [[opcodes_sec]] 124 | === 操作码(OPCODES) 125 | 智能合约的代码主要使用Solidity或Vyper等高级语言编写。编译器负责获取高级代码并创建它的低级解释,然后可以在以太坊虚拟机(EVM)上执行。编译器可以提取代码的最低表示(在EVM执行之前)是操作码。在这种情况下,需要高级语言(如Vyper)的每个实现来提供适当的编译机制(编译器)以允许(除其他之外)将高级代码编译到通用预定义的EVM操作码中。一个很好的例子是Vyper实现了以太坊的分片操作码。 126 | 127 | 128 | <<第十七章#,下一章:DevP2P协议>> 129 | 130 | image::images/thanks.jpeg["赞赏译者",height=400,align="center"] 131 | -------------------------------------------------------------------------------- /第四章.asciidoc: -------------------------------------------------------------------------------- 1 | [[testnets]] 2 | == 以太坊测试网(Testnets) 3 | 4 | <<第三章#,上一章:以太坊客户端>> 5 | 6 | === 什么是测试网? 7 | 8 | 测试网络(简称testnet)用于模拟以太网主网的行为。有一些公开的测试网络可以替代以太坊区块链。这些网络上的货币毫无价值,但它们仍然很有用,因为合约和协议变更的功能可以在不中断以太网主网或使用真实货币的情况下进行测试。当主网(简称mainnet)即将包含对以太坊协议的任何重大改变时,其测试主要在这些测试网络上完成。这些测试网络也被大量开发人员用于在部署到主网之前测试应用程序。 9 | 10 | === 使用 Testnets 11 | 12 | 你可以连接到公共可用的测试网络或创建你自己的私人测试网络。首先,让我们使用公共测试网来更简单地起步。要使用公共测试网络,需要一些测试网络以及到该网络的连接。对于testnet ether,使用“faucet”,faucet缓慢地分配测试ether,向任何询问的人“滴送”少量ether。要连接到一个测试网络,你需要一个以太坊客户端,完整的客户端,比如geth,或者完整的客户端的网关,比如MetaMask。 13 | 14 | === 获取测试以太网 15 | 16 | 由于测试网不以真正的金钱运作,矿工保护测试网的动机很弱。因此,测试网必须保护自己免受滥用和攻击。因此,为这些测试网创建了水龙头,以受控的方式向开发人员分发免费的测试ether(大多数faucet每隔几秒左右"滴注"ether)。这种以太网的受控分配可防止用户滥用链,因为提供有限的ether供应可防止他们向链中写入过多内容或执行太多交易。另外,一些testnets已经实施了认证证明(Proof of Authentication)方案,使用faucet需要具有适当社交媒体网站的认证的凭证。 17 | 18 | === 连接到Testnets 19 | 20 | ==== Metamask 21 | 22 | Metamask完全支持Ropsten,Kovan和Rinkeby测试网,但也可以连接到其他测试网和本地网。在Metamask中,只需单击“main network”下拉菜单,即可切换网络。MetaMask还提供了一个“buy”测试ether的选项,该选项将你引导至你可以请求免费测试以太网的faucet。如果使用Ropsten测试网,则可以从Ropsten测试faucet服务中获取ether。你可以从此页面访问此faucet。它需要Metamask扩展才能工作。https://faucet.metamask.io/ 23 | 24 | ===== Infura 25 | 26 | 当MetaMask连接到测试网络时,它使用Infura服务提供商来访问JSON-RPC接口。Infura诞生的目的是为ConsenSys内部项目提供稳定可靠的RPC访问。除了JSON-RPC API之外,Infura还提供REST(表述性状态转移)API,IPFS(星际文件系统,即去中心化存储)API和Websockets(即流式传输)API。 27 | 28 | Infura为Ethereum主网,Ropsten,Kovan,Rinkeby和INFURAnet(用于Infura的定制测试网络)提供网关API。 29 | 30 | 要通过MetaMask使用Infura进行较低级别的活动,你不需要账户。要直接使用API,你需要注册一个账户并使用Infura提供的API密钥。 31 | 32 | 有关Infura的更多信息,请访问: 33 | 34 | https://infura.io/ 35 | 36 | ==== Remix集成开发环境(IDE) 37 | 38 | Remix IDE可用于在主网和测试网上部署和交互智能合约,包括Ropsten,Rinkeby和Kovan(Web3提供者使用Infura地址和API密钥或通过Injected Web3使用MetaMask中选择的网络)和Ganache( Web3提供端点http://localhost:8545) 39 | 40 | https://github.com/ethereum/remix/blob/master/docs/run_tab.rst 41 | https://medium.com/swlh/deploy-smart-contracts-on-ropsten-testnet-through-ethereum-remix-233cd1494b4b 42 | 43 | ==== Geth 44 | Geth本身支持Ropsten和Rinkeby网络。要连接到Ropsten网络,请使用命令行参数: 45 | 46 | ---- 47 | geth --testnet 48 | ---- 49 | 50 | 这将开始同步Ropsten区块链。名为 +testnet+ 的新目录将在你的主Ethereum数据目录中创建。一个 +keystore+ 目录将在 +testnet+ 内部创建,并将存储你的testnet帐户的私钥。在撰写本文时,Ropsten区块链比以太坊主区块链小得多:大约14GB的数据。由于测试网需要的资源较少,因此首先在测试网上设置并测试你的代码会更简单。 51 | 52 | 与testnet的交互与mainnet类似。你可以使用控制台启动Geth testnet,方法是运行: 53 | ---- 54 | geth --testnet console 55 | ---- 56 | 57 | 这使得执行操作成为可能,例如开设新账户,检查余额,检查其他以太坊地址的余额等。 58 | 在Geth控制台之外运行时,只需将`--testnet`参数添加到命令行指令中,就可以执行类似于在主网上执行的操作。作为列举所有可用的testnet帐户及其地址的示例,请运行: 59 | 60 | ---- 61 | geth --testnet account list 62 | ---- 63 | 64 | [TIP] 65 | ==== 66 | 虽然小得多,但测试网仍需要一些时间才能完全同步。 67 | ==== 68 | 69 | 你可以通过在geth交互式控制台中运行以下命令来检查geth是否已完成同步测试网络: 70 | 71 | ---- 72 | eth.getBlock("latest").number 73 | ---- 74 | 75 | 一旦你的testnet节点完全同步,这应该返回一个非0的数字。你可以将该编号与已知的testnet区块浏览器中的最新块进行比较,例如https://ropsten.etherscan.io/ 76 | 77 | 同样,要连接到Rinkeby测试网络,请使用命令行参数: 78 | 79 | ---- 80 | geth --rinkeby 81 | ---- 82 | 83 | ==== Parity 84 | 85 | Parity客户端支持Ropsten和Kovan测试网络。你可以用+chain+参数选择你要连接的网络。例如,要同步Ropsten测试网络: 86 | 87 | ---- 88 | parity --chain ropsten 89 | ---- 90 | 91 | 同样,要同步Kovan测试网络,请使用: 92 | 93 | ---- 94 | parity --chain kovan 95 | ---- 96 | 97 | == 深入以太坊Testnets 98 | 99 | 在这个阶段你可能会想:“我明白我为什么要使用测试网络,但为什么会有这么多呢?” 100 | 101 | https://www.ethnews.com/ropsten-to-kovan-to-rinkeby-ethereums-testnet-troubles 102 | 103 | === 工作量证明(挖矿)Proof-of-Work (Mining) 与 权威证明(联合签名)Proof-of-Authority (Federated Signing) 104 | https://github.com/ethereum/guide/blob/master/poa.md 105 | 106 | === Morden(原始测试网) 107 | 108 | https://blog.ethereum.org/2016/11/20/from-morden-to-ropsten/ 109 | 110 | === Ropsten 111 | 112 | 如果你想开始在Ropsten网络上测试合约,有几个faucet可以供给你Ropsten的ether。如果faucet不起作用,请尝试不同的faucet。 113 | 114 | * http://faucet.ropsten.be:3001/ 115 | 这个faucet提供了应该排队接收测试以太的地址的可能性。 116 | 117 | * bitfwd Ropsten Faucet 118 | https://faucet.bitfwd.xyz/。 119 | 120 | * Kyber Network Ropsten Faucet 121 | https://faucet.kyber.network/。 122 | 123 | * MetaMask Ropsten Faucet 124 | https://faucet.metamask.io/ 125 | 126 | * Ropsten Testnet Mining Pool 127 | http://pool.ropsten.ethereum.org/ 128 | 129 | * Etherscan Ropsten Pool 130 | https://ropsten.etherscan.io/ 131 | 132 | === Rinkeby 133 | 134 | Rinkeby水龙头位于https://faucet.rinkeby.io/。 135 | 要请求测试ether,有必要在Twitter,Google Plus或Facebook上发布公开信息。https://www.rinkeby.io/ 136 | https://rinkeby.etherscan.io/ 137 | 138 | === Kovan 139 | 140 | Kovan testnet支持各种方法来请求测试ether。 141 | 更多信息可以在 https://github.com/kovan-testnet/faucet/blob/master/README.md 找到。 142 | 143 | https://medium.com/@Digix/announcing-kovan-a-stable-ethereum-public-testnet-10ac7cb6c85f 144 | 145 | https://kovan-testnet.github.io/website/ 146 | 147 | https://kovan.etherscan.io/ 148 | 149 | 150 | == 以太坊经典Testnets 151 | 152 | ==== Morden 153 | 154 | 以太坊经典目前运行着Morden测试网的一个变体,与以太坊经典活跃网络保持功能相同。你可以通过gastracker RPC或者为`geth`或`parity`提供一个标志来连接它. 155 | 156 | *Faucet:* http://testnet.epool.io/ 157 | 158 | *Gastracker RPC:* https://web3.gastracker.io/morden 159 | 160 | *Block Explorer:* http://mordenexplorer.ethertrack.io/home 161 | 162 | *Geth flag:* `geth --chain=morden` 163 | 164 | *Parity flag:* `parity --chain=classic-testnet` 165 | 166 | === 以太坊测试网的历史 167 | 168 | Olympic, Morden to Ropsten, Kovan, Rinkeby 169 | 170 | Olympic testnet (Network ID: 0) 是Frontier首个公共测试网(简称Ethereum 0.9)。它于2015年初推出,2015年中期被Morden取代时弃用。 171 | 172 | Ethereum’s Morden testnet (Network ID: 2) 与Frontier一起发布,从2015年7月开始运行,直到2016年11月不再使用。虽然任何使用以太坊的人都可以创建测试网,但Morden是第一个“官方”公共测试网,取代了Olympic测试网。由于臃肿区块链的长同步时间以及Geth和Parity客户端之间的共识问题,测试网络重新启动并重新生成为Ropsten。 173 | 174 | Ropsten (Network ID: 3) 是一个针对Homestead的公共跨客户端测试网,于2016年晚些时候推出,并作为公共测试网顺利运行至2017年2月底。根据Ethereum的核心开发人员PéterSzilágyi的说法,二月的时候,“恶意行为者决定滥用低PoW,并逐步将gas限制提高到90亿(从普通的470万),发送巨大交易损害了整个网络”。Ropsten在2017年3月被恢复。https://github.com/ethereum/ropsten 175 | 176 | Kovan (Network ID: 42) 是由Parity的权威证明(PoA)共识算法驱动的Homestead的公共Parity测试网络。该测试网不受垃圾邮件攻击的影响,因为ether供应由可信方控制。这些值得信赖的各方是在Ethereum上积极开发的公司。 177 | 尽管看起来这应该是以太坊测试网问题的解决方案,但在以太坊社区内似乎存在关于Kovan测试网的共识问题。https://github.com/kovan-testnet/proposal 178 | 179 | Rinkeby (Network ID: 4) 是由Ethereum团队于2017年4月开始的Homestead发布的Geth测试网络,并使用PoA共识协议。以斯德哥尔摩的地铁站命名,它几乎不受垃圾邮件攻击的影响(因为以太网供应由受信任方控制)。请参阅EIP 225:https://github.com/ethereum/EIPs/issues/225 180 | 181 | === 工作量证明(挖矿)Proof-of-Work (Mining) 与 权威证明(联合签名)Proof-of-Authority (Federated Signing) 182 | https://github.com/ethereum/guide/blob/master/poa.md 183 | 184 | Proof-of-Work 是一种协议,必须执行挖矿(昂贵的计算机计算)以在区块链(分布式账本)上创建新的区块(去信任的交易)。 185 | 缺点:能源消耗。集中的哈希算力与集中的采矿农场,不是真正的分布式。挖掘新块体所需的大量计算能力对环境有影响。 186 | 187 | Proof-of-Authority 是一种协议,它只将造币的负载分配给授权和可信的签名者,他们可以根据自己的判断并随时以发币频率分发新的区块。https://github.com/ethereum/EIPs/issues/225 188 | 优点:具有最显赫的身份的区块链参与者通过算法选择来验证块来交付交易。 189 | 190 | https://www.deepdotweb.com/2017/05/21/generalized-proof-activity-poa-forking-free-hybrid-consensus/ 191 | 192 | 193 | === 运行本地测试网 194 | 195 | ==== Ganache: 以太坊开发的个人区块链 196 | 197 | 你可以使用Ganache部署合约,开发应用程序并运行测试。它可用作Windows,Mac和Linux的桌面应用程序。 198 | 199 | 网站: http://truffleframework.com/ganache 200 | 201 | ==== Ganache CLI: Ganache 作为命令行工具。 202 | 203 | 这个工具以前称为“ethereumJS TestRPC”。 204 | 205 | https://github.com/trufflesuite/ganache-cli/ 206 | 207 | ---- 208 | $ npm install -g ganache-cli 209 | ---- 210 | 211 | 让我们开始以太坊区块链协议的节点模拟。 212 | * []检查`--networkId`和`--port`标志值是否与truffle.js中的配置相匹配 213 | * []检查`--gasLimit`标志值是否与https://ethstats.net上显示的最新主网gas极限(即8000000 gas)相匹配,以避免不必要地遇到`gas'异常。请注意,4000000000的“--gasPrice”代表4 gwei的gas价格。 214 | * []可以输入一个`--mnemonic'标志值来恢复以前的高清钱包和相关地址 215 | 216 | ---- 217 | $ ganache-cli \ 218 | --networkId=3 \ 219 | --port="8545" \ 220 | --verbose \ 221 | --gasLimit=8000000 \ 222 | --gasPrice=4000000000; 223 | ---- 224 | 225 | <<第五章#,下一章:密钥与地址>> 226 | 227 | image::images/thanks.jpeg["赞赏译者",height=400,align="center"] 228 | --------------------------------------------------------------------------------