├── Chapter01 ├── altering_data_on_blockchain.py ├── falsify_message.py ├── linked_list.py ├── nelsonkey.pem ├── nelsonkey.pub ├── simple_blockchain.py ├── simple_proof_of_work.py ├── validate_message.py └── verify_message.py ├── Chapter02 └── my_first_smart_contract │ ├── build │ └── contracts │ │ ├── Donation.json │ │ └── Migrations.json │ ├── contracts │ ├── Migrations.sol │ └── donation.sol │ ├── migrations │ ├── 1_initial_migration.js │ └── 2_deploy_donation.js │ └── truffle-config.js ├── Chapter03 ├── compiler.py ├── donation.vy ├── donation_call_hello_contract.vy └── hello.vy ├── Chapter04 ├── deploy_smart_contract_to_ganache.py ├── deploy_smart_contract_to_rinkeby.py ├── estimate_gas.py ├── extract_private_key.py ├── get_latest_nonce.py ├── play_with_smart_contract_in_ganache.py ├── play_with_smart_contract_in_rinkeby.py ├── send_money_ganache.py └── send_money_rinkeby.py ├── Chapter05 └── populus_tutorial │ ├── build │ └── contracts.json │ ├── contracts │ ├── Donation.vy │ ├── Greeter.vy │ └── Greeter2.vy │ ├── interact_smart_contract_in_ganache.py │ ├── interact_smart_contract_in_private_chain.py │ ├── project.json │ └── tests │ ├── __pycache__ │ ├── test_donation.cpython-36-PYTEST.pyc │ ├── test_greeter.cpython-36-PYTEST.pyc │ └── test_greeter2.cpython-36-PYTEST.pyc │ ├── test_donation.py │ ├── test_greeter.py │ └── test_greeter2.py ├── Chapter06 ├── voting_dapp │ ├── address.txt │ ├── client_address.txt │ ├── get_bytecode_from_smart_contract_address.py │ ├── simple_voting_client.py │ └── watch_simple_voting.py └── voting_project │ ├── 10_accounts.txt │ ├── build │ └── contracts.json │ ├── chains │ └── localblock │ │ ├── chain_data │ │ └── geth │ │ │ ├── chaindata │ │ │ ├── 000018.log │ │ │ ├── 000020.ldb │ │ │ ├── CURRENT │ │ │ ├── CURRENT.bak │ │ │ ├── LOG │ │ │ └── MANIFEST-000019 │ │ │ ├── lightchaindata │ │ │ ├── 000001.log │ │ │ ├── CURRENT │ │ │ ├── LOG │ │ │ └── MANIFEST-000000 │ │ │ ├── nodekey │ │ │ └── transactions.rlp │ │ ├── genesis.json │ │ ├── init_chain.sh │ │ ├── password │ │ └── run_chain.sh │ ├── create_10_accounts_on_private_chain.py │ ├── deploy_SmartVoting.py │ ├── distribute_money.py │ ├── get_bytecode_from_address.py │ ├── how_much_money_do_i_have.py │ └── project.json ├── Chapter07 ├── crash_course_qt_for_python │ ├── hello.py │ ├── hello_connect_param.py │ ├── hello_connect_signal_slot.py │ ├── hello_connect_simple.py │ ├── hello_custom_signal_slot.py │ ├── hello_horizontal_layout.py │ ├── hello_thread.py │ ├── hello_varieties.py │ ├── hello_vertical_horizontal_layout.py │ └── hello_vertical_layout.py ├── dapp │ ├── address.txt │ ├── fixtures.py │ └── twitter_dapp.py └── twitter_on_blockchain │ ├── build │ └── contracts.json │ ├── contracts │ └── TwitterOnBlockchain.vy │ ├── project.json │ └── tests │ └── test_twitter_on_blockchain.py ├── Chapter08 └── token_project │ ├── build │ └── contracts.json │ ├── chains │ └── localblock │ │ ├── genesis.json │ │ ├── init_chain.sh │ │ ├── password │ │ └── run_chain.sh │ ├── contracts │ ├── CrowdSaleToken.vy │ ├── ERC20Token.vy │ ├── SimpleToken.vy │ ├── SimpleTokenMint.vy │ └── StableCoin.vy │ ├── project.json │ └── tests │ ├── test_crowd_sale_token.py │ ├── test_erc20_token.py │ ├── test_simple_token.py │ └── test_stable_token.py ├── Chapter09 ├── advanced_course_qt │ ├── add_stretch.py │ ├── button_and_dialog.py │ ├── button_and_label.py │ ├── button_and_list.py │ ├── button_and_long_process.py │ ├── button_with_sizepolicy.py │ ├── combobox_and_label.py │ ├── create_grid_window.py │ ├── tabbed_window.py │ ├── test_button_and_dialog.py │ ├── test_button_and_label.py │ ├── test_button_and_list.py │ ├── test_button_and_long_process.py │ ├── test_combobox_and_label.py │ └── test_tabbed_window.py ├── chains │ ├── build │ │ └── contracts.json │ ├── contracts │ │ ├── ERC20Token.vy │ │ └── ERC20Token2.vy │ ├── localblock │ │ ├── genesis.json │ │ ├── init_chain.sh │ │ ├── password │ │ └── run_chain.sh │ ├── project.json │ └── registrar.json └── wallet │ ├── address.txt │ ├── blockchain.py │ ├── icons │ ├── ajax-loader.gif │ └── copy.svg │ ├── images │ ├── 427af7b53b8f56adf6f13932bb17c42ed2a53d04.png │ └── 6ad2ffd2e08bd73f5c50db60fdc82a58b0590b99.png │ ├── tests │ ├── test_account.py │ ├── test_send.py │ └── test_token.py │ ├── tools │ ├── identicon.py │ └── util.py │ ├── wallet.py │ ├── wallet_threads │ ├── balance_thread.py │ ├── send_thread.py │ └── send_token_thread.py │ └── wallet_widgets │ ├── account_widget.py │ ├── send_widget.py │ └── token_widget.py ├── Chapter10 ├── create_hash_from_content.py ├── hash_big_file.py ├── hash_directory.py ├── hello.txt ├── hello2.txt ├── hello3.txt ├── hello4.txt ├── hello_big.txt ├── merkle_dag.py ├── merkle_tree.py ├── sample_directory │ ├── hello.txt │ ├── hello2.txt │ ├── hello3.txt │ └── inner_directory │ │ └── hello4.txt └── test_merkle_dag_node.py ├── Chapter11 ├── add_directory.py ├── add_file.py ├── add_horoscope_predictions.py ├── add_image_file.py ├── cat.jpg ├── cat_cute_cat.py ├── construct_image_from_blocks.py ├── copy_in_mfs.py ├── crypto.proto ├── crypto_pb2.py ├── download_a_directory_of_cute_cat_picture.py ├── download_cute_cat_picture.py ├── exploring_mfs.py ├── generate_another_key.py ├── get_unicorn.py ├── get_unicorn_block.py ├── hello.txt ├── horoscope1.txt ├── horoscope2.txt ├── image_from_blocks.jpg ├── keys_list.py ├── list_blocks.py ├── merkledag.proto ├── merkledag_pb2.py ├── milada-vigerova-1284157-unsplash.jpg ├── mysite │ ├── README.md │ ├── img │ │ └── cat.jpg │ └── index.html ├── nude_picture.jpg ├── publish_horoscope1.py ├── publish_horoscope1_in_another_ipns.py ├── publish_horoscope2.py ├── publish_topic.py ├── removing_nude_picture.py ├── serialize_crypto_data.py ├── serialize_unicorn.py ├── subscribe_topic.py ├── unixfs.proto ├── unixfs_pb2.py └── unserialize_unicorn.py ├── Chapter12 ├── bootstrap_videos.py ├── check_bootstrap.py ├── decentralized_videos │ ├── decentralized_videos │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── manage.py │ └── videos │ │ ├── admin.py │ │ ├── apps.py │ │ ├── models.py │ │ ├── templates │ │ └── videos │ │ │ ├── base.html │ │ │ ├── channel.html │ │ │ ├── index.html │ │ │ ├── upload.html │ │ │ └── video.html │ │ ├── tests.py │ │ ├── urls.py │ │ └── views.py └── videos_sharing_smart_contract │ ├── build │ └── contracts.json │ ├── contracts │ └── VideosSharing.vy │ ├── project.json │ ├── registrar.json │ └── tests │ └── test_videos_sharing.py ├── LICENSE └── README.md /Chapter01/altering_data_on_blockchain.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | 4 | class Block: 5 | id = None 6 | history = None 7 | parent_id = None 8 | parent_hash = None 9 | 10 | block_A = Block() 11 | block_A.id = 1 12 | block_A.history = 'Nelson likes cat' 13 | 14 | block_B = Block() 15 | block_B.id = 2 16 | block_B.history = 'Marie likes dog' 17 | block_B.parent_id = block_A.id 18 | block_B.parent_hash = hashlib.sha256(json.dumps(block_A.__dict__).encode('utf-8')).hexdigest() 19 | 20 | block_C = Block() 21 | block_C.id = 3 22 | block_C.history = 'Marie likes dog' 23 | block_C.parent_id = block_B.id 24 | block_C.parent_hash = hashlib.sha256(json.dumps(block_B.__dict__).encode('utf-8')).hexdigest() 25 | 26 | print("Original parent hash in block C") 27 | print(block_C.parent_hash) 28 | print() 29 | 30 | print("Change data on block A") 31 | print() 32 | block_A.history = 'Nelson hates cat' 33 | block_B.parent_hash = hashlib.sha256(json.dumps(block_A.__dict__).encode('utf-8')).hexdigest() 34 | block_C.parent_hash = hashlib.sha256(json.dumps(block_B.__dict__).encode('utf-8')).hexdigest() 35 | 36 | print("New parent hash in block C which is different from original one") 37 | print(block_C.parent_hash) 38 | -------------------------------------------------------------------------------- /Chapter01/falsify_message.py: -------------------------------------------------------------------------------- 1 | from cryptography.hazmat.primitives import hashes 2 | from cryptography.hazmat.primitives.asymmetric import padding 3 | from cryptography.hazmat.backends import default_backend 4 | from cryptography.hazmat.primitives.asymmetric import rsa 5 | from cryptography.hazmat.primitives import serialization 6 | 7 | message = b'Nelson hates cat' 8 | signature = b'Fake Signature' 9 | 10 | with open("nelsonkey.pub", "rb") as key_file: 11 | public_key = serialization.load_pem_public_key( 12 | key_file.read(), 13 | backend=default_backend()) 14 | 15 | public_key.verify( 16 | signature, 17 | message, 18 | padding.PSS(mgf=padding.MGF1(hashes.SHA256()), 19 | salt_length=padding.PSS.MAX_LENGTH), 20 | hashes.SHA256()) 21 | -------------------------------------------------------------------------------- /Chapter01/linked_list.py: -------------------------------------------------------------------------------- 1 | class Block: 2 | id = None 3 | history = None 4 | parent_id = None 5 | 6 | block_A = Block() 7 | block_A.id = 1 8 | block_A.history = 'Nelson likes cat' 9 | 10 | block_B = Block() 11 | block_B.id = 2 12 | block_B.history = 'Marie likes dog' 13 | block_B.parent_id = block_A.id 14 | 15 | block_C = Block() 16 | block_C.id = 3 17 | block_C.history = 'Sky hates dog' 18 | block_C.parent_id = block_B.id 19 | -------------------------------------------------------------------------------- /Chapter01/nelsonkey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQDI7EwiWGrMg/jTkp2AYMYHqNiXq3npC17MnkdSsSg8/GwluUPs 3 | 56WfbvFMf/lJGHHs2jsApDGSRw10kjQrdu+cJC61978t93utokltqAby1l/VV4oS 4 | miPQ0vfIU9PGTGP4ZZNEaAaNfdRRG7q5TgzwIs52iEVQwzhqr4JYgwiEqQIDAQAB 5 | AoGAMPXz74KgM8oM/nGB0+DBIOiyLvNoN7O/nBkNxEbLSWExcVfeB4LDR0CfqEaf 6 | FoLQcCkVq6hXNBAAZWGjONw4suMUPFAR7LyAyxsdevxnOsh0CVM2FapqCZ9DRjOl 7 | 4vFKpHtrnGCYb5s3L6aduOkP1JDRnypD4oj0BFCesg8E6KECQQDxwNg4/pFB+FFW 8 | hm2XKuXl2aHGBj/1zDueY0ZmF7OvToamILIZqiOykBl7HwiHTFTvcvYJ/KAWn8mw 9 | XPxrMMjVAkEA1MN5+Bqn9I0EUw/g+Ct5+yexGIePTsdHmVzcGVXCaZHfjX8UBJwU 10 | 1x2y/3O4GwbKtAsxDNajtV8wW/nPrKZ2hQJBAJaWkFlYQTU9r7TdAbDTOOdnHD0U 11 | sYyzjkOIBVcc1oGRwZAD98zCzX+yAR1PZGma46FQE/B+m13cZK7mDRncBo0CQH+5 12 | xcvivLc7vL/chXKWLwlOX6tZHfft/BaeP2daK3bS4oCALFVEGeK16pIeNwTKg/+n 13 | xE9vvyGPvmlIw5pi6D0CQQDKSV1VSWh2M7XIfxWnJqKd+qbr8x1IU0MJsPFmBwOW 14 | EwS111GZYpHqQ1bf12XaUZ92jEZ87DdXvcHmjxekpvig 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /Chapter01/nelsonkey.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDI7EwiWGrMg/jTkp2AYMYHqNiX 3 | q3npC17MnkdSsSg8/GwluUPs56WfbvFMf/lJGHHs2jsApDGSRw10kjQrdu+cJC61 4 | 978t93utokltqAby1l/VV4oSmiPQ0vfIU9PGTGP4ZZNEaAaNfdRRG7q5TgzwIs52 5 | iEVQwzhqr4JYgwiEqQIDAQAB 6 | -----END PUBLIC KEY----- 7 | -------------------------------------------------------------------------------- /Chapter01/simple_blockchain.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | 4 | class Block: 5 | id = None 6 | history = None 7 | parent_id = None 8 | parent_hash = None 9 | 10 | block_A = Block() 11 | block_A.id = 1 12 | block_A.history = 'Nelson likes cat' 13 | 14 | block_B = Block() 15 | block_B.id = 2 16 | block_B.history = 'Marie likes dog' 17 | block_B.parent_id = block_A.id 18 | block_B.parent_hash = hashlib.sha256(json.dumps(block_A.__dict__).encode('utf-8')).hexdigest() 19 | 20 | block_C = Block() 21 | block_C.id = 3 22 | block_C.history = 'Marie likes dog' 23 | block_C.parent_id = block_B.id 24 | block_C.parent_hash = hashlib.sha256(json.dumps(block_B.__dict__).encode('utf-8')).hexdigest() 25 | -------------------------------------------------------------------------------- /Chapter01/simple_proof_of_work.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | 3 | payload_string = '{"history": "Sky loves turtle", "parent_id": 3, "id": 4}' 4 | payload_bytes = bytes(payload_string, 'utf-8') 5 | for i in range(1000000): 6 | nonce = str(i).encode('utf-8') 7 | result = hashlib.sha256(payload_bytes + nonce).hexdigest() 8 | if result[0:5] == '00000': 9 | print("The answer to puzzle: " + str(i)) 10 | print("Input to hash is: " + payload_string + str(i)) 11 | print("Output hash which has 5 leading zeros: " + result) 12 | break 13 | 14 | -------------------------------------------------------------------------------- /Chapter01/validate_message.py: -------------------------------------------------------------------------------- 1 | from cryptography.hazmat.primitives import hashes 2 | from cryptography.hazmat.primitives.asymmetric import padding 3 | from cryptography.hazmat.backends import default_backend 4 | from cryptography.hazmat.primitives.asymmetric import rsa 5 | from cryptography.hazmat.primitives import serialization 6 | 7 | def fetch_public_key(user): 8 | with open(user.decode('ascii') + "key.pub", "rb") as key_file: 9 | public_key = serialization.load_pem_public_key( 10 | key_file.read(), 11 | backend=default_backend()) 12 | return public_key 13 | 14 | # Message coming from user 15 | message = b"Nelson likes cat" 16 | 17 | # Signature coming from user 18 | signature = b'7\xe1\xbe\xff\xa8\xe5\'{\xe7\x97w\xfa\x87c\x19\xf0T\xca\xcd\x13\xe0\x80\xa3<\xed\x8b\x1f\x98\x19f\xe4\x00S\xe4\xed \x99Q\x15\x93\xb3\xf1\xe0\\\x03\xbe`\x9ab\xdc+\x9a\xb9\x00\x19\xf4\xe0\xa4a\x17i0HD\xe6~\\\x952\xec5\x18I\xd8k&\x13\xdcY"\xb9o\xa9\xe0\xf2\xa7\x8e\t\xa1PF\xd0\x8a\x10p\xe8\xcd\xc3\xe6f\x93\x9a\xe0\x7f\xbb\xe2\xd6HVM:\xd1\xcfG\xf6\x98gm$\xdf^\xf4\xae\xe4\xd5\xea\xb5\xb4' 19 | 20 | user = message.split()[0].lower() 21 | # fetch public key from Nelson 22 | public_key = fetch_public_key(user) 23 | # … verify the message like before 24 | public_key.verify( 25 | signature, 26 | message, 27 | padding.PSS(mgf=padding.MGF1(hashes.SHA256()), 28 | salt_length=padding.PSS.MAX_LENGTH), 29 | hashes.SHA256()) 30 | -------------------------------------------------------------------------------- /Chapter01/verify_message.py: -------------------------------------------------------------------------------- 1 | from cryptography.hazmat.primitives import hashes 2 | from cryptography.hazmat.primitives.asymmetric import padding 3 | from cryptography.hazmat.backends import default_backend 4 | from cryptography.hazmat.primitives.asymmetric import rsa 5 | from cryptography.hazmat.primitives import serialization 6 | 7 | # Configuration 8 | GENERATE_PRIVATE_KEY = False 9 | DERIVE_PUBLIC_KEY_FROM_PRIVATE_KEY = False 10 | PRIVATE_KEY_FILE = "nelsonkey.pem" 11 | PUBLIC_KEY_FILE = "nelsonkey.pub" 12 | MESSAGE = b"Nelson likes cat" 13 | 14 | if GENERATE_PRIVATE_KEY: 15 | # Generate private key 16 | private_key = rsa.generate_private_key( 17 | public_exponent=65537, 18 | key_size=2048, 19 | backend=default_backend() 20 | ) 21 | else: 22 | # Load private key from pem file 23 | with open(PRIVATE_KEY_FILE, "rb") as key_file: 24 | private_key = serialization.load_pem_private_key( 25 | key_file.read(), 26 | password=None, 27 | backend=default_backend() 28 | ) 29 | 30 | signature = private_key.sign( 31 | MESSAGE, 32 | padding.PSS( 33 | mgf=padding.MGF1(hashes.SHA256()), 34 | salt_length=padding.PSS.MAX_LENGTH 35 | ), 36 | hashes.SHA256() 37 | ) 38 | 39 | 40 | if DERIVE_PUBLIC_KEY_FROM_PRIVATE_KEY: 41 | # Getting public key from private key 42 | public_key = private_key.public_key() 43 | else: 44 | # Load public key from file 45 | with open(PUBLIC_KEY_FILE, "rb") as key_file: 46 | public_key = serialization.load_pem_public_key( 47 | key_file.read(), 48 | backend=default_backend() 49 | ) 50 | 51 | public_key.verify( 52 | signature, 53 | MESSAGE, 54 | padding.PSS( 55 | mgf=padding.MGF1(hashes.SHA256()), 56 | salt_length=padding.PSS.MAX_LENGTH 57 | ), 58 | hashes.SHA256() 59 | ) 60 | 61 | print(signature) 62 | -------------------------------------------------------------------------------- /Chapter02/my_first_smart_contract/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.6.0; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 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 | -------------------------------------------------------------------------------- /Chapter02/my_first_smart_contract/contracts/donation.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | contract Donation { 4 | address public donatur; 5 | address payable donatee; 6 | uint public money; 7 | string public useless_variable; 8 | 9 | constructor() public { 10 | donatee = msg.sender; 11 | useless_variable = "Donation string"; 12 | } 13 | 14 | function change_useless_variable(string memory param) public { 15 | useless_variable = param; 16 | } 17 | 18 | function donate() public payable { 19 | donatur = msg.sender; 20 | money = msg.value; 21 | } 22 | 23 | function receive_donation() public { 24 | donatee.transfer(address(this).balance); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chapter02/my_first_smart_contract/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /Chapter02/my_first_smart_contract/migrations/2_deploy_donation.js: -------------------------------------------------------------------------------- 1 | var Donation = artifacts.require("./Donation.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Donation); 5 | }; 6 | -------------------------------------------------------------------------------- /Chapter02/my_first_smart_contract/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | "development": { 4 | network_id: 5777, 5 | host: "localhost", 6 | port: 7545 7 | }, 8 | }, 9 | mocha: { 10 | }, 11 | compilers: { 12 | solc: { 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /Chapter03/compiler.py: -------------------------------------------------------------------------------- 1 | import vyper 2 | import os, json 3 | 4 | filename = 'hello.vy' 5 | contract_name = 'Hello' 6 | contract_json_file = open('Hello.json', 'w') 7 | 8 | with open(filename, 'r') as f: 9 | content = f.read() 10 | 11 | current_directory = 'haha' # os.curdir 12 | 13 | smart_contract = {} 14 | smart_contract[current_directory] = content 15 | 16 | format = ['abi', 'bytecode'] 17 | compiled_code = vyper.compile_codes(smart_contract, format, 'dict') 18 | 19 | smart_contract_json = { 20 | 'contractName': contract_name, 21 | 'abi': compiled_code[current_directory]['abi'], 22 | 'bytecode': compiled_code[current_directory]['bytecode'] 23 | } 24 | 25 | json.dump(smart_contract_json, contract_json_file) 26 | 27 | contract_json_file.close() 28 | -------------------------------------------------------------------------------- /Chapter03/donation.vy: -------------------------------------------------------------------------------- 1 | struct DonaturDetail: 2 | sum: uint256(wei) 3 | name: bytes[100] 4 | time: timestamp 5 | 6 | donatur_details: public(map(address, DonaturDetail)) 7 | 8 | donaturs: public(address[10]) 9 | 10 | donatee: public(address) 11 | 12 | index: int128 13 | 14 | @public 15 | def __init__(): 16 | self.donatee = msg.sender 17 | 18 | @payable 19 | @public 20 | def donate(name: bytes[100]): 21 | assert msg.value >= as_wei_value(1, "ether") 22 | assert self.index < 10 23 | 24 | self.donatur_details[msg.sender] = DonaturDetail({ 25 | sum: msg.value, 26 | name: name, 27 | time: block.timestamp 28 | }) 29 | 30 | self.donaturs[self.index] = msg.sender 31 | self.index += 1 32 | 33 | @public 34 | def withdraw_donation(): 35 | assert msg.sender == self.donatee 36 | 37 | send(self.donatee, self.balance) 38 | -------------------------------------------------------------------------------- /Chapter03/donation_call_hello_contract.vy: -------------------------------------------------------------------------------- 1 | struct DonaturDetail: 2 | sum: uint256(wei) 3 | name: bytes[100] 4 | time: timestamp 5 | 6 | contract Hello(): 7 | def say_hello() -> bytes[32]: constant 8 | 9 | donatur_details: public(map(address, DonaturDetail)) 10 | 11 | infinite_array_of_strings: map(uint256, bytes[100]) 12 | 13 | mapping_of_mapping_of_mapping: map(uint256, map(uint256, map(uint256, bytes[10]))) 14 | 15 | donaturs: public(address[10]) 16 | 17 | donatee: public(address) 18 | 19 | index: int128 20 | 21 | @public 22 | def __init__(): 23 | self.donatee = msg.sender 24 | 25 | @payable 26 | @public 27 | def donate(name: bytes[100]): 28 | assert msg.value >= as_wei_value(1, "ether") 29 | assert self.index < 10 30 | 31 | self.donatur_details[msg.sender] = DonaturDetail({ 32 | sum: msg.value, 33 | name: name, 34 | time: block.timestamp 35 | }) 36 | 37 | self.donaturs[self.index] = msg.sender 38 | self.index += 1 39 | 40 | @public 41 | def withdraw_donation(): 42 | assert msg.sender == self.donatee 43 | 44 | send(self.donatee, self.balance) 45 | 46 | @public 47 | @constant 48 | def donation_smart_contract_call_hello_smart_contract_method(smart_contract_address: address) -> bytes[32]: 49 | return Hello(smart_contract_address).say_hello() 50 | -------------------------------------------------------------------------------- /Chapter03/hello.vy: -------------------------------------------------------------------------------- 1 | name: public(bytes[24]) 2 | 3 | @public 4 | def __init__(): 5 | self.name = "Satoshi Nakamoto" 6 | 7 | @public 8 | def change_name(new_name: bytes[24]): 9 | self.name = new_name 10 | 11 | @public 12 | def say_hello() -> bytes[32]: 13 | return concat("Hello, ", self.name) 14 | -------------------------------------------------------------------------------- /Chapter04/deploy_smart_contract_to_ganache.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, HTTPProvider 2 | from vyper import compile_codes 3 | 4 | 5 | contract_source_code = ''' 6 | name: public(bytes[24]) 7 | 8 | @public 9 | def __init__(): 10 | self.name = "Satoshi Nakamoto" 11 | 12 | @public 13 | def change_name(new_name: bytes[24]): 14 | self.name = new_name 15 | 16 | @public 17 | def say_hello() -> bytes[32]: 18 | return concat("Hello, ", self.name) 19 | ''' 20 | 21 | smart_contract = {} 22 | smart_contract['hello'] = contract_source_code 23 | 24 | format = ['abi', 'bytecode'] 25 | compiled_code = compile_codes(smart_contract, format, 'dict') 26 | 27 | bytecode = compiled_code['hello']['bytecode'] 28 | abi = compiled_code['hello']['abi'] 29 | 30 | w3 = Web3(HTTPProvider('http://localhost:7545')) 31 | 32 | HelloSmartContract = w3.eth.contract(abi=abi, bytecode=bytecode) 33 | 34 | # Change the account to your situation. 35 | tx_hash = HelloSmartContract.constructor().transact({'from': '0xb105F01Ce341Ef9282dc2201BDfdA2c26903da77'}) 36 | 37 | tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) 38 | print(tx_receipt) 39 | -------------------------------------------------------------------------------- /Chapter04/deploy_smart_contract_to_rinkeby.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, IPCProvider 2 | from vyper import compile_codes 3 | 4 | 5 | contract_source_code = ''' 6 | name: public(bytes[24]) 7 | 8 | @public 9 | def __init__(): 10 | self.name = "Satoshi Nakamoto" 11 | 12 | @public 13 | def change_name(new_name: bytes[24]): 14 | self.name = new_name 15 | 16 | @public 17 | def say_hello() -> bytes[32]: 18 | return concat("Hello, ", self.name) 19 | ''' 20 | 21 | smart_contract = {} 22 | smart_contract['hello'] = contract_source_code 23 | 24 | format = ['abi', 'bytecode'] 25 | compiled_code = compile_codes(smart_contract, format, 'dict') 26 | 27 | bytecode = compiled_code['hello']['bytecode'] 28 | abi = compiled_code['hello']['abi'] 29 | 30 | # Change the path of geth.ipc according to your situation. 31 | w3 = Web3(IPCProvider('/opt/data/ethereumdata/geth.ipc')) 32 | 33 | HelloSmartContract = w3.eth.contract(abi=abi, bytecode=bytecode) 34 | 35 | # Change the account, the password, and the path to the keystore according to your situation, 36 | from_account = "0x28f5b56b035da966afa609f65fd8f7d71ff68327" 37 | password = 'password123' 38 | with open('/opt/data/ethereumdata/keystore/UTC--2018-10-12T09-30-20.687898000Z--28f5b56b035da966afa609f65fd8f7d71ff68327') as keyfile: 39 | encrypted_key = keyfile.read() 40 | private_key = w3.eth.account.decrypt(encrypted_key, password) 41 | 42 | nonce = w3.eth.getTransactionCount(Web3.toChecksumAddress(from_account)) 43 | 44 | transaction = HelloSmartContract.constructor().buildTransaction({'from': Web3.toChecksumAddress(from_account), 45 | 'gas': 500000, 46 | 'gasPrice': w3.toWei('30', 'gwei'), 47 | 'nonce': nonce}) 48 | 49 | signed = w3.eth.account.signTransaction(transaction, private_key) 50 | tx_hash = w3.eth.sendRawTransaction(signed.rawTransaction) 51 | 52 | tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) 53 | print(tx_receipt) 54 | -------------------------------------------------------------------------------- /Chapter04/estimate_gas.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, HTTPProvider 2 | 3 | 4 | w3 = Web3(HTTPProvider('http://localhost:7545')) 5 | 6 | transaction = { 7 | 'to': Web3.toChecksumAddress('0x9049386D4d5808e0Cd9e294F2aA3d70F01Fbf0C5'), 8 | 'value': w3.toWei('1', 'ether'), 9 | 'gas': 100000, 10 | 'gasPrice': w3.toWei('1', 'gwei'), 11 | 'nonce': 0 12 | } 13 | 14 | print("Estimating gas usage: " + str(w3.eth.estimateGas(transaction))) 15 | print("Gas price: " + str(w3.eth.gasPrice)) 16 | -------------------------------------------------------------------------------- /Chapter04/extract_private_key.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3 2 | w3 = Web3() 3 | 4 | # Change the filepath to your keystore's filepath 5 | with open('/opt/data/ethereumdata/keystore/UTC--2018-10-12T09-30-20.687898000Z--28f5b56b035da966afa609f65fd8f7d71ff68327') as keyfile: 6 | encrypted_key = keyfile.read() 7 | private_key = w3.eth.account.decrypt(encrypted_key, 'password123') 8 | print(private_key) 9 | -------------------------------------------------------------------------------- /Chapter04/get_latest_nonce.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, HTTPProvider 2 | 3 | 4 | web3 = Web3(HTTPProvider('http://localhost:7545')) 5 | 6 | nonce = web3.eth.getTransactionCount("0x6d3eBC3000d112B70aaCA8F770B06f961C852014") 7 | print(nonce) 8 | -------------------------------------------------------------------------------- /Chapter04/play_with_smart_contract_in_ganache.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, HTTPProvider 2 | from vyper import compile_codes 3 | 4 | 5 | contract_source_code = ''' 6 | name: public(bytes[24]) 7 | 8 | @public 9 | def __init__(): 10 | self.name = "Satoshi Nakamoto" 11 | 12 | @public 13 | def change_name(new_name: bytes[24]): 14 | self.name = new_name 15 | 16 | @public 17 | def say_hello() -> bytes[32]: 18 | return concat("Hello, ", self.name) 19 | ''' 20 | 21 | smart_contract = {} 22 | smart_contract['hello'] = contract_source_code 23 | 24 | format = ['abi', 'bytecode'] 25 | compiled_code = compile_codes(smart_contract, format, 'dict') 26 | 27 | abi = compiled_code['hello']['abi'] 28 | 29 | w3 = Web3(HTTPProvider('http://localhost:7545')) 30 | 31 | # Change the address of the smart contract, the private key, and the account according to your situation 32 | address = "0x9Dc44aa8d05c86388E647F954D00CaA858837804" 33 | private_key = '0x1a369cedacf0bf2f5fd16b5215527e8c8767cbd761ebefa28d9df0d389c60b6e' 34 | w3.eth.defaultAccount = '0xb105F01Ce341Ef9282dc2201BDfdA2c26903da77' 35 | 36 | Hello = w3.eth.contract(address=address, abi=abi) 37 | 38 | print(Hello.functions.name().call()) 39 | 40 | print(Hello.functions.say_hello().call()) 41 | 42 | nonce = w3.eth.getTransactionCount(w3.eth.defaultAccount) 43 | 44 | txn = Hello.functions.change_name(b"Lionel Messi").buildTransaction({ 45 | 'gas': 70000, 46 | 'gasPrice': w3.toWei('1', 'gwei'), 47 | 'nonce': nonce 48 | }) 49 | 50 | signed_txn = w3.eth.account.signTransaction(txn, private_key=private_key) 51 | 52 | signed_txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction) 53 | 54 | w3.eth.waitForTransactionReceipt(signed_txn_hash) 55 | 56 | print(Hello.functions.say_hello().call()) 57 | -------------------------------------------------------------------------------- /Chapter04/play_with_smart_contract_in_rinkeby.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, IPCProvider 2 | from vyper import compile_codes 3 | 4 | 5 | contract_source_code = ''' 6 | name: public(bytes[24]) 7 | 8 | @public 9 | def __init__(): 10 | self.name = "Satoshi Nakamoto" 11 | 12 | @public 13 | def change_name(new_name: bytes[24]): 14 | self.name = new_name 15 | 16 | @public 17 | def say_hello() -> bytes[32]: 18 | return concat("Hello, ", self.name) 19 | ''' 20 | 21 | smart_contract = {} 22 | smart_contract['hello'] = contract_source_code 23 | 24 | format = ['abi', 'bytecode'] 25 | compiled_code = compile_codes(smart_contract, format, 'dict') 26 | 27 | abi = compiled_code['hello']['abi'] 28 | 29 | # Change the path of geth.ipc according to your situation. 30 | w3 = Web3(IPCProvider('/opt/data/ethereumdata/geth.ipc')) 31 | 32 | from web3.middleware import geth_poa_middleware 33 | w3.middleware_stack.inject(geth_poa_middleware, layer=0) 34 | 35 | # Change the address of the smart contract, the account, the password, and the path to the keystore according to your situation, 36 | address = "0x58705EBBc791DB917c7771FdA6175b2D9F59D51A" 37 | password = 'password123' 38 | w3.eth.defaultAccount = '0x28f5b56b035da966afa609f65fd8f7d71ff68327' 39 | with open('/opt/data/ethereumdata/keystore/UTC--2018-10-12T09-30-20.687898000Z--28f5b56b035da966afa609f65fd8f7d71ff68327') as keyfile: 40 | encrypted_key = keyfile.read() 41 | private_key = w3.eth.account.decrypt(encrypted_key, password) 42 | 43 | Hello = w3.eth.contract(address=address, abi=abi) 44 | 45 | print(Hello.functions.name().call()) 46 | 47 | print(Hello.functions.say_hello().call()) 48 | 49 | nonce = w3.eth.getTransactionCount(w3.eth.defaultAccount) 50 | 51 | txn = Hello.functions.change_name(b"Lionel Messi").buildTransaction({ 52 | 'gas': 500000, 53 | 'gasPrice': w3.toWei('30', 'gwei'), 54 | 'nonce': nonce 55 | }) 56 | 57 | signed_txn = w3.eth.account.signTransaction(txn, private_key=private_key) 58 | 59 | signed_txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction) 60 | 61 | w3.eth.waitForTransactionReceipt(signed_txn_hash) 62 | 63 | print(Hello.functions.say_hello().call()) 64 | -------------------------------------------------------------------------------- /Chapter04/send_money_ganache.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, HTTPProvider 2 | 3 | 4 | w3 = Web3(HTTPProvider('http://localhost:7545')) 5 | 6 | private_key = '59e31694256f71b8d181f47fc67914798c4b96990e835fc1407bf4673ead30e2' 7 | 8 | transaction = { 9 | 'to': Web3.toChecksumAddress('0x9049386D4d5808e0Cd9e294F2aA3d70F01Fbf0C5'), 10 | 'value': w3.toWei('1', 'ether'), 11 | 'gas': 100000, 12 | 'gasPrice': w3.toWei('1', 'gwei'), 13 | 'nonce': 0 14 | } 15 | 16 | signed = w3.eth.account.signTransaction(transaction, private_key) 17 | tx = w3.eth.sendRawTransaction(signed.rawTransaction) 18 | -------------------------------------------------------------------------------- /Chapter04/send_money_rinkeby.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, IPCProvider 2 | from web3.middleware import geth_poa_middleware 3 | 4 | # Change the path of geth.ipc according to your situation. 5 | w3 = Web3(IPCProvider('/opt/data/ethereumdata/geth.ipc')) 6 | 7 | w3.middleware_stack.inject(geth_poa_middleware, layer=0) 8 | 9 | # Change the destination account, the sender account, the password, and the path to the keystore according to your situation, 10 | password = 'password123' 11 | from_account = "0x28f5b56b035da966afa609f65fd8f7d71ff68327" 12 | to_account = '0x99fb2eee85acbf878d4154de73d5fb1b7e88c328' 13 | with open('/opt/data/ethereumdata/keystore/UTC--2018-10-12T09-30-20.687898000Z--28f5b56b035da966afa609f65fd8f7d71ff68327') as keyfile: 14 | encrypted_key = keyfile.read() 15 | private_key = w3.eth.account.decrypt(encrypted_key, password) 16 | 17 | nonce = w3.eth.getTransactionCount(Web3.toChecksumAddress(from_account)) 18 | 19 | transaction = { 20 | 'to': Web3.toChecksumAddress(to_account), 21 | 'value': w3.toWei('1', 'ether'), 22 | 'gas': 21000, 23 | 'gasPrice': w3.toWei('2', 'gwei'), 24 | 'nonce': nonce 25 | } 26 | 27 | signed = w3.eth.account.signTransaction(transaction, private_key) 28 | w3.eth.sendRawTransaction(signed.rawTransaction) 29 | -------------------------------------------------------------------------------- /Chapter05/populus_tutorial/contracts/Donation.vy: -------------------------------------------------------------------------------- 1 | struct DonaturDetail: 2 | sum: uint256(wei) 3 | name: bytes[100] 4 | time: timestamp 5 | 6 | donatur_details: public(map(address, DonaturDetail)) 7 | 8 | donaturs: public(address[10]) 9 | 10 | donatee: public(address) 11 | 12 | index: int128 13 | 14 | @public 15 | def __init__(): 16 | self.donatee = msg.sender 17 | 18 | @payable 19 | @public 20 | def donate(name: bytes[100]): 21 | assert msg.value >= as_wei_value(1, "ether") 22 | assert self.index < 10 23 | 24 | self.donatur_details[msg.sender] = DonaturDetail({ 25 | sum: msg.value, 26 | name: name, 27 | time: block.timestamp 28 | }) 29 | 30 | self.donaturs[self.index] = msg.sender 31 | self.index += 1 32 | 33 | @public 34 | def withdraw_donation(): 35 | assert msg.sender == self.donatee 36 | 37 | send(self.donatee, self.balance) 38 | -------------------------------------------------------------------------------- /Chapter05/populus_tutorial/contracts/Greeter.vy: -------------------------------------------------------------------------------- 1 | greeting: bytes[20] 2 | 3 | 4 | @public 5 | def __init__(): 6 | self.greeting = "Hello" 7 | 8 | 9 | @public 10 | def setGreeting(x: bytes[20]): 11 | self.greeting = x 12 | 13 | 14 | @public 15 | def greet() -> bytes[20]: 16 | return self.greeting 17 | -------------------------------------------------------------------------------- /Chapter05/populus_tutorial/contracts/Greeter2.vy: -------------------------------------------------------------------------------- 1 | greeting: bytes[20] 2 | 3 | 4 | @public 5 | def __init__(greeting_param: bytes[20]): 6 | self.greeting = greeting_param 7 | 8 | 9 | @public 10 | def setGreeting(x: bytes[20]): 11 | self.greeting = x 12 | 13 | 14 | @public 15 | def greet() -> bytes[20]: 16 | return self.greeting 17 | -------------------------------------------------------------------------------- /Chapter05/populus_tutorial/interact_smart_contract_in_ganache.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, HTTPProvider 2 | w3 = Web3(HTTPProvider("http://localhost:7545")) 3 | 4 | print(w3.eth.coinbase) 5 | print(w3.eth.getBalance(w3.eth.coinbase)) 6 | 7 | # Change this address to your smart contract address 8 | address = "0x9Dc44aa8d05c86388E647F954D00CaA858837804" 9 | false = False 10 | true = True 11 | abi = [ 12 | { 13 | "constant": false, 14 | "inputs": [], 15 | "name": "__init__", 16 | "outputs": [], 17 | "payable": false, 18 | "type": "constructor" 19 | }, 20 | { 21 | "constant": false, 22 | "gas": 317989, 23 | "inputs": [ 24 | { 25 | "name": "name", 26 | "type": "bytes" 27 | } 28 | ], 29 | "name": "donate", 30 | "outputs": [], 31 | "payable": true, 32 | "type": "function" 33 | }, 34 | { 35 | "constant": false, 36 | "gas": 35996, 37 | "inputs": [], 38 | "name": "withdraw_donation", 39 | "outputs": [], 40 | "payable": false, 41 | "type": "function" 42 | }, 43 | { 44 | "constant": true, 45 | "gas": 793, 46 | "inputs": [ 47 | { 48 | "name": "arg0", 49 | "type": "address" 50 | } 51 | ], 52 | "name": "donatur_details__sum", 53 | "outputs": [ 54 | { 55 | "name": "out", 56 | "type": "uint256" 57 | } 58 | ], 59 | "payable": false, 60 | "type": "function" 61 | }, 62 | { 63 | "constant": true, 64 | "gas": 18462, 65 | "inputs": [ 66 | { 67 | "name": "arg0", 68 | "type": "address" 69 | } 70 | ], 71 | "name": "donatur_details__name", 72 | "outputs": [ 73 | { 74 | "name": "out", 75 | "type": "bytes" 76 | } 77 | ], 78 | "payable": false, 79 | "type": "function" 80 | }, 81 | { 82 | "constant": true, 83 | "gas": 853, 84 | "inputs": [ 85 | { 86 | "name": "arg0", 87 | "type": "address" 88 | } 89 | ], 90 | "name": "donatur_details__time", 91 | "outputs": [ 92 | { 93 | "name": "out", 94 | "type": "uint256" 95 | } 96 | ], 97 | "payable": false, 98 | "type": "function" 99 | }, 100 | { 101 | "constant": true, 102 | "gas": 850, 103 | "inputs": [ 104 | { 105 | "name": "arg0", 106 | "type": "int128" 107 | } 108 | ], 109 | "name": "donaturs", 110 | "outputs": [ 111 | { 112 | "name": "out", 113 | "type": "address" 114 | } 115 | ], 116 | "payable": false, 117 | "type": "function" 118 | }, 119 | { 120 | "constant": true, 121 | "gas": 663, 122 | "inputs": [], 123 | "name": "donatee", 124 | "outputs": [ 125 | { 126 | "name": "out", 127 | "type": "address" 128 | } 129 | ], 130 | "payable": false, 131 | "type": "function" 132 | } 133 | ] 134 | 135 | donation = w3.eth.contract(address=address, abi=abi) 136 | print(donation.functions.donatee().call()) 137 | -------------------------------------------------------------------------------- /Chapter05/populus_tutorial/interact_smart_contract_in_private_chain.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, IPCProvider 2 | w3 = Web3(IPCProvider(ipc_path="/tmp/geth.ipc")) 3 | 4 | print(w3.eth.coinbase) 5 | print(w3.eth.getBalance(w3.eth.coinbase)) 6 | 7 | # Change this address to your smart contract address 8 | address = "0xab3B30CFeC1D50DCb0a13671D09d55e63b7cFf40" 9 | false = False 10 | true = True 11 | abi = [ 12 | { 13 | "constant": false, 14 | "inputs": [], 15 | "name": "__init__", 16 | "outputs": [], 17 | "payable": false, 18 | "type": "constructor" 19 | }, 20 | { 21 | "constant": false, 22 | "gas": 317989, 23 | "inputs": [ 24 | { 25 | "name": "name", 26 | "type": "bytes" 27 | } 28 | ], 29 | "name": "donate", 30 | "outputs": [], 31 | "payable": true, 32 | "type": "function" 33 | }, 34 | { 35 | "constant": false, 36 | "gas": 35996, 37 | "inputs": [], 38 | "name": "withdraw_donation", 39 | "outputs": [], 40 | "payable": false, 41 | "type": "function" 42 | }, 43 | { 44 | "constant": true, 45 | "gas": 793, 46 | "inputs": [ 47 | { 48 | "name": "arg0", 49 | "type": "address" 50 | } 51 | ], 52 | "name": "donatur_details__sum", 53 | "outputs": [ 54 | { 55 | "name": "out", 56 | "type": "uint256" 57 | } 58 | ], 59 | "payable": false, 60 | "type": "function" 61 | }, 62 | { 63 | "constant": true, 64 | "gas": 18462, 65 | "inputs": [ 66 | { 67 | "name": "arg0", 68 | "type": "address" 69 | } 70 | ], 71 | "name": "donatur_details__name", 72 | "outputs": [ 73 | { 74 | "name": "out", 75 | "type": "bytes" 76 | } 77 | ], 78 | "payable": false, 79 | "type": "function" 80 | }, 81 | { 82 | "constant": true, 83 | "gas": 853, 84 | "inputs": [ 85 | { 86 | "name": "arg0", 87 | "type": "address" 88 | } 89 | ], 90 | "name": "donatur_details__time", 91 | "outputs": [ 92 | { 93 | "name": "out", 94 | "type": "uint256" 95 | } 96 | ], 97 | "payable": false, 98 | "type": "function" 99 | }, 100 | { 101 | "constant": true, 102 | "gas": 850, 103 | "inputs": [ 104 | { 105 | "name": "arg0", 106 | "type": "int128" 107 | } 108 | ], 109 | "name": "donaturs", 110 | "outputs": [ 111 | { 112 | "name": "out", 113 | "type": "address" 114 | } 115 | ], 116 | "payable": false, 117 | "type": "function" 118 | }, 119 | { 120 | "constant": true, 121 | "gas": 663, 122 | "inputs": [], 123 | "name": "donatee", 124 | "outputs": [ 125 | { 126 | "name": "out", 127 | "type": "address" 128 | } 129 | ], 130 | "payable": false, 131 | "type": "function" 132 | } 133 | ] 134 | 135 | donation = w3.eth.contract(address=address, abi=abi) 136 | print(donation.functions.donatee().call()) 137 | -------------------------------------------------------------------------------- /Chapter05/populus_tutorial/tests/__pycache__/test_donation.cpython-36-PYTEST.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter05/populus_tutorial/tests/__pycache__/test_donation.cpython-36-PYTEST.pyc -------------------------------------------------------------------------------- /Chapter05/populus_tutorial/tests/__pycache__/test_greeter.cpython-36-PYTEST.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter05/populus_tutorial/tests/__pycache__/test_greeter.cpython-36-PYTEST.pyc -------------------------------------------------------------------------------- /Chapter05/populus_tutorial/tests/__pycache__/test_greeter2.cpython-36-PYTEST.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter05/populus_tutorial/tests/__pycache__/test_greeter2.cpython-36-PYTEST.pyc -------------------------------------------------------------------------------- /Chapter05/populus_tutorial/tests/test_donation.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import eth_tester 3 | 4 | 5 | def test_donatee(web3, chain): 6 | donation, _ = chain.provider.get_or_deploy_contract('Donation') 7 | 8 | donatee = donation.functions.donatee().call() 9 | assert donatee == web3.eth.coinbase 10 | 11 | def test_donate_less_than_1_eth(web3, chain): 12 | donation, _ = chain.provider.get_or_deploy_contract('Donation') 13 | 14 | with pytest.raises(eth_tester.exceptions.TransactionFailed): 15 | donation.functions.donate(b'Taylor Swift').transact({'value': web3.toWei('0.8', 'ether')}) 16 | 17 | def test_donate_1_eth(web3, chain): 18 | import time 19 | 20 | donation, _ = chain.provider.get_or_deploy_contract('Donation') 21 | 22 | t = eth_tester.EthereumTester() 23 | account2 = t.get_accounts()[1] 24 | 25 | donatur_name = b'Taylor Swift' 26 | set_txn_hash = donation.functions.donate(donatur_name).transact({'from': account2, 'value': web3.toWei('1', 'ether')}) 27 | chain.wait.for_receipt(set_txn_hash) 28 | 29 | donatur = donation.functions.donaturs(0).call() 30 | donation_sum = donation.functions.donatur_details__sum(donatur).call() 31 | donation_name = donation.functions.donatur_details__name(donatur).call() 32 | donation_time = donation.functions.donatur_details__time(donatur).call() 33 | 34 | assert donatur == account2 35 | assert donation_sum == web3.toWei('1', 'ether') 36 | assert donation_name == donatur_name 37 | assert (int(time.time()) - donation_time) < 600 # could be flaky 38 | 39 | assert web3.eth.getBalance(donation.address) == web3.toWei('1', 'ether') 40 | 41 | def test_other_account_could_not_withdraw_money(web3, chain): 42 | donation, _ = chain.provider.get_or_deploy_contract('Donation') 43 | 44 | t = eth_tester.EthereumTester() 45 | account2 = t.get_accounts()[1] 46 | 47 | donatur_name = b'Taylor Swift' 48 | set_txn_hash = donation.functions.donate(donatur_name).transact({'from': account2, 'value': web3.toWei('1', 'ether')}) 49 | chain.wait.for_receipt(set_txn_hash) 50 | 51 | with pytest.raises(eth_tester.exceptions.TransactionFailed): 52 | donation.functions.withdraw_donation().transact({'from': account2}) 53 | 54 | def test_manager_account_could_withdraw_money(web3, chain): 55 | donation, _ = chain.provider.get_or_deploy_contract('Donation') 56 | 57 | t = eth_tester.EthereumTester() 58 | account2 = t.get_accounts()[1] 59 | 60 | donatur_name = b'Taylor Swift' 61 | set_txn_hash = donation.functions.donate(donatur_name).transact({'from': account2, 'value': web3.toWei('1', 'ether')}) 62 | chain.wait.for_receipt(set_txn_hash) 63 | 64 | initial_balance = web3.eth.getBalance(web3.eth.coinbase) 65 | set_txn_hash = donation.functions.withdraw_donation().transact({'from': web3.eth.coinbase}) 66 | chain.wait.for_receipt(set_txn_hash) 67 | 68 | after_withdraw_balance = web3.eth.getBalance(web3.eth.coinbase) 69 | 70 | assert abs((after_withdraw_balance - initial_balance) - web3.toWei('1', 'ether')) < web3.toWei('10', 'gwei') 71 | -------------------------------------------------------------------------------- /Chapter05/populus_tutorial/tests/test_greeter.py: -------------------------------------------------------------------------------- 1 | def test_greeter(chain): 2 | greeter, _ = chain.provider.get_or_deploy_contract('Greeter') 3 | 4 | greeting = greeter.functions.greet().call() 5 | assert greeting == b'Hello' 6 | 7 | 8 | def test_custom_greeting(chain): 9 | greeter, _ = chain.provider.get_or_deploy_contract('Greeter') 10 | 11 | set_txn_hash = greeter.functions.setGreeting(b'Guten Tag').transact() 12 | chain.wait.for_receipt(set_txn_hash) 13 | 14 | greeting = greeter.functions.greet().call() 15 | assert greeting == b'Guten Tag' 16 | -------------------------------------------------------------------------------- /Chapter05/populus_tutorial/tests/test_greeter2.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | @pytest.fixture() 4 | def greeter2_contract(chain): 5 | Greeter2Factory = chain.provider.get_contract_factory('Greeter2') 6 | deploy_txn_hash = Greeter2Factory.constructor(b'Hola').transact() 7 | contract_address = chain.wait.for_contract_address(deploy_txn_hash) 8 | return Greeter2Factory(address=contract_address) 9 | 10 | def test_greeter2(greeter2_contract): 11 | greeting2 = greeter2_contract.functions.greet().call() 12 | assert greeting2 == b'Hola' 13 | -------------------------------------------------------------------------------- /Chapter06/voting_dapp/address.txt: -------------------------------------------------------------------------------- 1 | 0x993FFADB39D323D8B134F6f0CdD83d510c45D306 2 | -------------------------------------------------------------------------------- /Chapter06/voting_dapp/client_address.txt: -------------------------------------------------------------------------------- 1 | 86B84a2cCCFfef5c8B2c30E6366674A6C1613BFb 2 | -------------------------------------------------------------------------------- /Chapter06/voting_dapp/get_bytecode_from_smart_contract_address.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, IPCProvider 2 | 3 | w3 = Web3(IPCProvider(ipc_path='/tmp/geth.ipc')) 4 | 5 | with open('address.txt', 'r') as f: 6 | content = f.read().rstrip("\n") 7 | 8 | address = content 9 | 10 | 11 | -------------------------------------------------------------------------------- /Chapter06/voting_dapp/simple_voting_client.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, IPCProvider 2 | from populus.utils.wait import wait_for_transaction_receipt 3 | import glob 4 | 5 | w3 = Web3(IPCProvider(ipc_path='/tmp/geth.ipc')) 6 | 7 | with open('client_address.txt', 'r') as f: 8 | content = f.read().rstrip("\n") 9 | 10 | address = content.lower() 11 | 12 | encrypted_private_key_file = glob.glob('../voting_project/chains/localblock/chain_data/keystore/*' + address)[0] 13 | with open(encrypted_private_key_file) as f: 14 | password = 'password123' 15 | private_key = w3.eth.account.decrypt(f.read(), password) 16 | w3.eth.defaultAccount = '0x' + address 17 | 18 | 19 | false = False 20 | true = True 21 | abi = [ 22 | { 23 | "anonymous": false, 24 | "inputs": [ 25 | { 26 | "indexed": true, 27 | "name": "_from", 28 | "type": "address" 29 | }, 30 | { 31 | "indexed": false, 32 | "name": "_proposal", 33 | "type": "int128" 34 | } 35 | ], 36 | "name": "Voting", 37 | "type": "event" 38 | }, 39 | { 40 | "constant": false, 41 | "inputs": [ 42 | { 43 | "name": "_proposalNames", 44 | "type": "bytes32[2]" 45 | } 46 | ], 47 | "name": "__init__", 48 | "outputs": [], 49 | "payable": false, 50 | "type": "constructor" 51 | }, 52 | { 53 | "constant": false, 54 | "gas": 73421, 55 | "inputs": [ 56 | { 57 | "name": "proposal", 58 | "type": "int128" 59 | } 60 | ], 61 | "name": "vote", 62 | "outputs": [], 63 | "payable": false, 64 | "type": "function" 65 | }, 66 | { 67 | "constant": true, 68 | "gas": 4006, 69 | "inputs": [], 70 | "name": "winner_name", 71 | "outputs": [ 72 | { 73 | "name": "out", 74 | "type": "bytes32" 75 | } 76 | ], 77 | "payable": false, 78 | "type": "function" 79 | }, 80 | { 81 | "constant": true, 82 | "gas": 868, 83 | "inputs": [ 84 | { 85 | "name": "arg0", 86 | "type": "int128" 87 | } 88 | ], 89 | "name": "proposals__name", 90 | "outputs": [ 91 | { 92 | "name": "out", 93 | "type": "bytes32" 94 | } 95 | ], 96 | "payable": false, 97 | "type": "function" 98 | }, 99 | { 100 | "constant": true, 101 | "gas": 904, 102 | "inputs": [ 103 | { 104 | "name": "arg0", 105 | "type": "int128" 106 | } 107 | ], 108 | "name": "proposals__vote_count", 109 | "outputs": [ 110 | { 111 | "name": "out", 112 | "type": "int128" 113 | } 114 | ], 115 | "payable": false, 116 | "type": "function" 117 | }, 118 | { 119 | "constant": true, 120 | "gas": 633, 121 | "inputs": [], 122 | "name": "proposals_count", 123 | "outputs": [ 124 | { 125 | "name": "out", 126 | "type": "int128" 127 | } 128 | ], 129 | "payable": false, 130 | "type": "function" 131 | }, 132 | { 133 | "constant": true, 134 | "gas": 835, 135 | "inputs": [ 136 | { 137 | "name": "arg0", 138 | "type": "address" 139 | } 140 | ], 141 | "name": "voters_voted", 142 | "outputs": [ 143 | { 144 | "name": "out", 145 | "type": "int128" 146 | } 147 | ], 148 | "payable": false, 149 | "type": "function" 150 | } 151 | ] 152 | 153 | with open('address.txt', 'r') as f: 154 | content = f.read().rstrip("\n") 155 | 156 | smart_contract_address = content 157 | 158 | SimpleVoting = w3.eth.contract(address=smart_contract_address, abi=abi) 159 | 160 | nonce = w3.eth.getTransactionCount(Web3.toChecksumAddress(w3.eth.defaultAccount)) 161 | 162 | txn = SimpleVoting.functions.vote(0).buildTransaction({ 163 | 'gas': 70000, 164 | 'gasPrice': w3.toWei('1', 'gwei'), 165 | 'nonce': nonce 166 | }) 167 | 168 | signed = w3.eth.account.signTransaction(txn, private_key=private_key) 169 | w3.eth.sendRawTransaction(signed.rawTransaction) 170 | -------------------------------------------------------------------------------- /Chapter06/voting_dapp/watch_simple_voting.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, IPCProvider 2 | 3 | w3 = Web3(IPCProvider(ipc_path='/tmp/geth.ipc')) 4 | 5 | false = False 6 | true = True 7 | abi = [ 8 | { 9 | "anonymous": false, 10 | "inputs": [ 11 | { 12 | "indexed": true, 13 | "name": "_from", 14 | "type": "address" 15 | }, 16 | { 17 | "indexed": false, 18 | "name": "_proposal", 19 | "type": "int128" 20 | } 21 | ], 22 | "name": "Voting", 23 | "type": "event" 24 | }, 25 | { 26 | "constant": false, 27 | "inputs": [ 28 | { 29 | "name": "_proposalNames", 30 | "type": "bytes32[2]" 31 | } 32 | ], 33 | "name": "__init__", 34 | "outputs": [], 35 | "payable": false, 36 | "type": "constructor" 37 | }, 38 | { 39 | "constant": false, 40 | "gas": 73421, 41 | "inputs": [ 42 | { 43 | "name": "proposal", 44 | "type": "int128" 45 | } 46 | ], 47 | "name": "vote", 48 | "outputs": [], 49 | "payable": false, 50 | "type": "function" 51 | }, 52 | { 53 | "constant": true, 54 | "gas": 4006, 55 | "inputs": [], 56 | "name": "winner_name", 57 | "outputs": [ 58 | { 59 | "name": "out", 60 | "type": "bytes32" 61 | } 62 | ], 63 | "payable": false, 64 | "type": "function" 65 | }, 66 | { 67 | "constant": true, 68 | "gas": 868, 69 | "inputs": [ 70 | { 71 | "name": "arg0", 72 | "type": "int128" 73 | } 74 | ], 75 | "name": "proposals__name", 76 | "outputs": [ 77 | { 78 | "name": "out", 79 | "type": "bytes32" 80 | } 81 | ], 82 | "payable": false, 83 | "type": "function" 84 | }, 85 | { 86 | "constant": true, 87 | "gas": 904, 88 | "inputs": [ 89 | { 90 | "name": "arg0", 91 | "type": "int128" 92 | } 93 | ], 94 | "name": "proposals__vote_count", 95 | "outputs": [ 96 | { 97 | "name": "out", 98 | "type": "int128" 99 | } 100 | ], 101 | "payable": false, 102 | "type": "function" 103 | }, 104 | { 105 | "constant": true, 106 | "gas": 633, 107 | "inputs": [], 108 | "name": "proposals_count", 109 | "outputs": [ 110 | { 111 | "name": "out", 112 | "type": "int128" 113 | } 114 | ], 115 | "payable": false, 116 | "type": "function" 117 | }, 118 | { 119 | "constant": true, 120 | "gas": 835, 121 | "inputs": [ 122 | { 123 | "name": "arg0", 124 | "type": "address" 125 | } 126 | ], 127 | "name": "voters_voted", 128 | "outputs": [ 129 | { 130 | "name": "out", 131 | "type": "int128" 132 | } 133 | ], 134 | "payable": false, 135 | "type": "function" 136 | } 137 | ] 138 | 139 | with open('address.txt', 'r') as f: 140 | content = f.read().rstrip("\n") 141 | 142 | address = content 143 | 144 | SimpleVoting = w3.eth.contract(address=address, abi=abi) 145 | 146 | event_filter = SimpleVoting.events.Voting.createFilter(fromBlock=1) 147 | 148 | import time 149 | while True: 150 | print(event_filter.get_new_entries()) 151 | time.sleep(2) 152 | -------------------------------------------------------------------------------- /Chapter06/voting_project/10_accounts.txt: -------------------------------------------------------------------------------- 1 | 0x139D95864176F56401439C449c12f2d4b36712F6 2 | 0x36461a003a03f857D60f5bD0B8e8a64aAB4e4535 3 | 0xDED72BA25f492bE5AB1cE806128D4Cc0F2033d10 4 | 0x2DD74d520Fe64180be7dBB25edBb5f44de0b59aA 5 | 0x2327AE719A2edc554F7Aef0153D5bc45a0385146 6 | 0xd52F4502fCC1AAAD26c6D7BAfF0b79B9692c523F 7 | 0xB98463474Ddc78ea54050dFAbc6F55964Fd01DA2 8 | 0x093bbE836281B6eCb0777dc3405f893CeDB30Ef2 9 | 0xf0738EF5635f947f13dD41F34DAe6B2caa0a9EA6 10 | 0x86B84a2cCCFfef5c8B2c30E6366674A6C1613BFb 11 | -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/chain_data/geth/chaindata/000018.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter06/voting_project/chains/localblock/chain_data/geth/chaindata/000018.log -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/chain_data/geth/chaindata/000020.ldb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter06/voting_project/chains/localblock/chain_data/geth/chaindata/000020.ldb -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/chain_data/geth/chaindata/CURRENT: -------------------------------------------------------------------------------- 1 | MANIFEST-000019 2 | -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/chain_data/geth/chaindata/CURRENT.bak: -------------------------------------------------------------------------------- 1 | MANIFEST-000015 2 | -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/chain_data/geth/chaindata/LOG: -------------------------------------------------------------------------------- 1 | =============== Oct 26, 2018 (WIB) =============== 2 | 11:48:20.287058 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed 3 | 11:48:20.306720 db@open opening 4 | 11:48:20.307440 version@stat F·[] S·0B[] Sc·[] 5 | 11:48:20.311804 db@janitor F·2 G·0 6 | 11:48:20.311824 db@open done T·5.089308ms 7 | =============== Oct 26, 2018 (WIB) =============== 8 | 11:49:02.553588 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed 9 | 11:49:02.553669 version@stat F·[] S·0B[] Sc·[] 10 | 11:49:02.553674 db@open opening 11 | 11:49:02.553694 journal@recovery F·1 12 | 11:49:02.555412 journal@recovery recovering @1 13 | 11:49:02.565866 memdb@flush created L0@2 N·11 S·731B "H\xb5\b..\xab\xaa,,v5":"sec..M\xdd(,v1" 14 | 11:49:02.567636 version@stat F·[1] S·731B[731B] Sc·[0.25] 15 | 11:49:02.586294 db@janitor F·3 G·0 16 | 11:49:02.586387 db@open done T·32.665273ms 17 | 13:53:07.088257 db@close closing 18 | 13:53:07.088291 db@close done T·33.33µs 19 | =============== Oct 26, 2018 (WIB) =============== 20 | 20:03:43.106112 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed 21 | 20:03:43.106455 version@stat F·[1] S·731B[731B] Sc·[0.25] 22 | 20:03:43.106504 db@open opening 23 | 20:03:43.106532 journal@recovery F·1 24 | 20:03:43.108429 journal@recovery recovering @3 25 | 20:03:43.163144 memdb@flush created L0@5 N·24059 S·1MiB "\x0f6g..$\x88\xc9,v24065":"\xeck\xfe..\xe7a],v24058" 26 | 20:03:43.171252 version@stat F·[2] S·1MiB[1MiB] Sc·[0.50] 27 | 20:03:43.188234 db@janitor F·4 G·0 28 | 20:03:43.188319 db@open done T·81.796148ms 29 | 20:05:18.855138 table@compaction L0·2 -> L1·0 S·1MiB Q·24236 30 | 20:05:18.873398 table@build created L1@8 N·16056 S·906KiB "\x0f6g..$\x88\xc9,v24065":"\xeck\xfe..\xe7a],v24058" 31 | 20:05:18.873439 version@stat F·[0 1] S·906KiB[0B 906KiB] Sc·[0.00 0.01] 32 | 20:05:18.877846 table@compaction committed F-1 S-326KiB Ke·0 D·8014 T·22.683932ms 33 | 20:05:18.877998 table@remove removed @5 34 | 20:05:18.878026 table@remove removed @2 35 | 20:43:54.884573 db@close closing 36 | 20:43:54.884651 db@close done T·77.894µs 37 | =============== Oct 27, 2018 (WIB) =============== 38 | 11:56:01.291389 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed 39 | 11:56:01.292154 version@stat F·[0 1] S·906KiB[0B 906KiB] Sc·[0.00 0.01] 40 | 11:56:01.292202 db@open opening 41 | 11:56:01.292356 journal@recovery F·1 42 | 11:56:01.311475 journal@recovery recovering @6 43 | 11:56:01.328042 memdb@flush created L0@9 N·4066 S·216KiB "\x02\xe1\xfb..Z\x98c,v28126":"\xfe\rk..\x90\x99\xbc,v28122" 44 | 11:56:01.329709 version@stat F·[1 1] S·1MiB[216KiB 906KiB] Sc·[0.25 0.01] 45 | 11:56:01.344880 db@janitor F·4 G·0 46 | 11:56:01.344944 db@open done T·52.721075ms 47 | 11:59:51.021745 table@compaction L0·1 -> L1·1 S·1MiB Q·28541 48 | 11:59:51.046420 table@build created L1@12 N·18783 S·1MiB "\x02\xe1\xfb..Z\x98c,v28126":"\xfe\rk..\x90\x99\xbc,v28122" 49 | 11:59:51.046453 version@stat F·[0 1] S·1MiB[0B 1MiB] Sc·[0.00 0.01] 50 | 11:59:51.050251 table@compaction committed F-1 S-55KiB Ke·0 D·1339 T·28.480342ms 51 | 11:59:51.050312 table@remove removed @9 52 | 11:59:51.050384 table@remove removed @8 53 | 14:24:40.662602 db@close closing 54 | 14:24:40.662692 db@close done T·87.907µs 55 | =============== Oct 27, 2018 (WIB) =============== 56 | 15:30:59.475369 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed 57 | 15:30:59.475471 version@stat F·[0 1] S·1MiB[0B 1MiB] Sc·[0.00 0.01] 58 | 15:30:59.475477 db@open opening 59 | 15:30:59.475502 journal@recovery F·1 60 | 15:30:59.477125 journal@recovery recovering @10 61 | 15:30:59.504038 memdb@flush created L0@13 N·13822 S·633KiB "\x0e&r..!\xac\x15,v41958":"\xe9\b\xc7..bTW,v41956" 62 | 15:30:59.511229 version@stat F·[1 1] S·1MiB[633KiB 1MiB] Sc·[0.25 0.01] 63 | 15:30:59.527157 db@janitor F·4 G·0 64 | 15:30:59.527231 db@open done T·51.737007ms 65 | 15:31:46.525245 table@compaction L0·1 -> L1·1 S·1MiB Q·42029 66 | 15:31:46.560132 table@build created L1@16 N·28681 S·1MiB "\x02\xe1\xfb..Z\x98c,v28126":"\xfe\rk..\x90\x99\xbc,v28122" 67 | 15:31:46.560177 version@stat F·[0 1] S·1MiB[0B 1MiB] Sc·[0.00 0.02] 68 | 15:31:46.566003 table@compaction committed F-1 S-160KiB Ke·0 D·3924 T·40.723055ms 69 | 15:31:46.566138 table@remove removed @13 70 | 15:31:46.566292 table@remove removed @12 71 | 17:41:08.645866 db@close closing 72 | 17:41:08.645901 db@close done T·34.519µs 73 | =============== Oct 29, 2018 (WIB) =============== 74 | 10:28:09.147552 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed 75 | 10:28:09.148330 version@stat F·[0 1] S·1MiB[0B 1MiB] Sc·[0.00 0.02] 76 | 10:28:09.148371 db@open opening 77 | 10:28:09.148580 journal@recovery F·1 78 | 10:28:09.177398 journal@recovery recovering @14 79 | 10:28:09.226443 memdb@flush created L0@17 N·8085 S·417KiB "\vm=..\xe1\xf5\x10,v50041":"\xf7ڪ..M\x17\xc1,v50030" 80 | 10:28:09.229265 version@stat F·[1 1] S·1MiB[417KiB 1MiB] Sc·[0.25 0.02] 81 | 10:28:09.243206 db@janitor F·4 G·0 82 | 10:28:09.243232 db@open done T·94.841332ms 83 | 10:33:29.722386 table@compaction L0·1 -> L1·1 S·1MiB Q·50447 84 | 10:33:29.754540 table@build created L1@20 N·34073 S·1MiB "\x02\xe1\xfb..Z\x98c,v28126":"\xfe\rk..\x90\x99\xbc,v28122" 85 | 10:33:29.754575 version@stat F·[0 1] S·1MiB[0B 1MiB] Sc·[0.00 0.02] 86 | 10:33:29.758481 table@compaction committed F-1 S-110KiB Ke·0 D·2693 T·36.06689ms 87 | 10:33:29.758555 table@remove removed @17 88 | 10:33:29.758658 table@remove removed @16 89 | 14:34:08.617509 db@close closing 90 | 14:34:08.617544 db@close done T·34.076µs 91 | -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/chain_data/geth/chaindata/MANIFEST-000019: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter06/voting_project/chains/localblock/chain_data/geth/chaindata/MANIFEST-000019 -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/chain_data/geth/lightchaindata/000001.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter06/voting_project/chains/localblock/chain_data/geth/lightchaindata/000001.log -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/chain_data/geth/lightchaindata/CURRENT: -------------------------------------------------------------------------------- 1 | MANIFEST-000000 2 | -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/chain_data/geth/lightchaindata/LOG: -------------------------------------------------------------------------------- 1 | =============== Oct 26, 2018 (WIB) =============== 2 | 11:48:20.312326 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed 3 | 11:48:20.320592 db@open opening 4 | 11:48:20.321784 version@stat F·[] S·0B[] Sc·[] 5 | 11:48:20.326414 db@janitor F·2 G·0 6 | 11:48:20.326458 db@open done T·5.835614ms 7 | -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/chain_data/geth/lightchaindata/MANIFEST-000000: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter06/voting_project/chains/localblock/chain_data/geth/lightchaindata/MANIFEST-000000 -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/chain_data/geth/nodekey: -------------------------------------------------------------------------------- 1 | 6b65f7d4bd98293f043182ad032cef6499827d6c69c6c71dc29a4c3e91549935 -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/chain_data/geth/transactions.rlp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter06/voting_project/chains/localblock/chain_data/geth/transactions.rlp -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/genesis.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 4 | "coinbase": "0xfa146d7af4b92eb1751c3c9c644fa436a60f7b75", 5 | "extraData": "0x686f727365", 6 | "config": { 7 | "daoForkBlock": 0, 8 | "daoForSupport": true, 9 | "homesteadBlock": 0 10 | }, 11 | "timestamp": "0x0", 12 | "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", 13 | "nonce": "0xdeadbeefdeadbeef", 14 | "alloc": { 15 | "0xfa146d7af4b92eb1751c3c9c644fa436a60f7b75":{ 16 | "balance": "1000000000000000000000000000000" 17 | } 18 | }, 19 | "gasLimit": "0x47d5cc", 20 | "difficulty": "0x01" 21 | } 22 | -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/init_chain.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | geth --rpc --rpcaddr 127.0.0.1 --rpcport 8545 --rpcapi admin,debug,eth,miner,net,personal,shh,txpool,web3,ws --ws --wsaddr 127.0.0.1 --wsport 8546 --wsapi admin,debug,eth,miner,net,personal,shh,txpool,web3,ws --datadir /home/arjuna/Documents/WritingBook/hands-on-blockchain-for-python-developers/chapter_06/voting_project/chains/localblock/chain_data --maxpeers 0 --networkid 1234 --port 30303 --ipcpath /tmp/tmpvnbzbt29/geth.ipc --nodiscover --mine --minerthreads 1 init /home/arjuna/Documents/WritingBook/hands-on-blockchain-for-python-developers/chapter_06/voting_project/chains/localblock/genesis.json -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/password: -------------------------------------------------------------------------------- 1 | this-is-not-a-secure-password 2 | -------------------------------------------------------------------------------- /Chapter06/voting_project/chains/localblock/run_chain.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | geth --rpc --rpcaddr 127.0.0.1 --rpcport 8545 --rpcapi admin,debug,eth,miner,net,personal,shh,txpool,web3,ws --ws --wsaddr 127.0.0.1 --wsport 8546 --wsapi admin,debug,eth,miner,net,personal,shh,txpool,web3,ws --datadir /home/arjuna/Documents/WritingBook/hands-on-blockchain-for-python-developers/chapter_06/voting_project/chains/localblock/chain_data --maxpeers 0 --networkid 1234 --port 30303 --ipcpath /tmp/tmpvnbzbt29/geth.ipc --unlock 0xfa146d7af4b92eb1751c3c9c644fa436a60f7b75 --password /home/arjuna/Documents/WritingBook/hands-on-blockchain-for-python-developers/chapter_06/voting_project/chains/localblock/password --nodiscover --mine --minerthreads 1 --ipcpath /tmp/geth.ipc 3 | -------------------------------------------------------------------------------- /Chapter06/voting_project/create_10_accounts_on_private_chain.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, IPCProvider 2 | 3 | w3 = Web3(IPCProvider(ipc_path='/tmp/geth.ipc')) 4 | 5 | with open('10_accounts.txt', 'w') as f: 6 | for i in range(10): 7 | f.write(w3.personal.newAccount('password123') + "\n") 8 | -------------------------------------------------------------------------------- /Chapter06/voting_project/deploy_SmartVoting.py: -------------------------------------------------------------------------------- 1 | from populus import Project 2 | from populus.utils.wait import wait_for_transaction_receipt 3 | 4 | 5 | def main(): 6 | 7 | project = Project() 8 | 9 | chain_name = "ganache" 10 | 11 | with project.get_chain(chain_name) as chain: 12 | 13 | SimpleVoting = chain.provider.get_contract_factory('SimpleVoting') 14 | 15 | txhash = SimpleVoting.deploy(transaction={"from": chain.web3.eth.coinbase}, args=[[b'Messi', b'Ronaldo']]) 16 | receipt = wait_for_transaction_receipt(chain.web3, txhash) 17 | simple_voting_address = receipt["contractAddress"] 18 | print("SimpleVoting contract address is", simple_voting_address) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | 24 | -------------------------------------------------------------------------------- /Chapter06/voting_project/distribute_money.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, IPCProvider 2 | from populus.utils.wait import wait_for_transaction_receipt 3 | import glob 4 | 5 | w3 = Web3(IPCProvider(ipc_path='/tmp/geth.ipc')) 6 | 7 | address = 'fa146d7af4b92eb1751c3c9c644fa436a60f7b75' 8 | 9 | with open('chains/localblock/password') as f: 10 | password = f.read().rstrip("\n") 11 | 12 | encrypted_private_key_file = glob.glob('chains/localblock/chain_data/keystore/*' + address)[0] 13 | with open(encrypted_private_key_file) as f2: 14 | private_key = w3.eth.account.decrypt(f2.read(), password) 15 | 16 | w3.eth.defaultAccount = w3.eth.coinbase 17 | 18 | with open('10_accounts.txt', 'r') as f: 19 | accounts = f.readlines() 20 | for account in accounts: 21 | nonce = w3.eth.getTransactionCount(Web3.toChecksumAddress(w3.eth.defaultAccount)) 22 | transaction = { 23 | 'to': Web3.toChecksumAddress(account.rstrip("\n")), 24 | 'value': w3.toWei('10', 'ether'), 25 | 'gas': 1000000, 26 | 'gasPrice': w3.toWei('20', 'gwei'), 27 | 'nonce': nonce 28 | } 29 | 30 | signed = w3.eth.account.signTransaction(transaction, private_key) 31 | txhash = w3.eth.sendRawTransaction(signed.rawTransaction) 32 | wait_for_transaction_receipt(w3, txhash) 33 | 34 | -------------------------------------------------------------------------------- /Chapter06/voting_project/get_bytecode_from_address.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, HTTPProvider 2 | 3 | w3 = Web3(HTTPProvider('http://127.0.0.1:8545')) 4 | print(w3.eth.getCode('0x891dfe5Dbf551E090805CEee41b94bB2205Bdd17')) 5 | -------------------------------------------------------------------------------- /Chapter06/voting_project/how_much_money_do_i_have.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, IPCProvider 2 | 3 | w3 = Web3(IPCProvider(ipc_path='/tmp/geth.ipc')) 4 | 5 | print(w3.fromWei(w3.eth.getBalance(w3.eth.coinbase), 'ether')) 6 | -------------------------------------------------------------------------------- /Chapter07/crash_course_qt_for_python/hello.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PySide2.QtWidgets import QApplication, QWidget 3 | 4 | app = QApplication(sys.argv) 5 | 6 | window = QWidget() 7 | window.resize(400, 400) 8 | 9 | window.show() 10 | sys.exit(app.exec_()) 11 | -------------------------------------------------------------------------------- /Chapter07/crash_course_qt_for_python/hello_connect_param.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit, QLabel 3 | from PySide2 import QtCore 4 | 5 | app = QApplication(sys.argv) 6 | 7 | hello_line_edit = QLineEdit() 8 | world_label = QLabel("") 9 | 10 | layout = QVBoxLayout() 11 | layout.addWidget(hello_line_edit) 12 | layout.addWidget(world_label) 13 | 14 | def set_world_label(text): 15 | world_label.setText(text.upper()) 16 | 17 | hello_line_edit.textChanged.connect(set_world_label) 18 | #hello_line_edit.connect(QtCore.SIGNAL('textChanged(QString)'), set_world_label) 19 | 20 | window = QWidget() 21 | window.setLayout(layout) 22 | window.resize(200, 200) 23 | 24 | window.show() 25 | sys.exit(app.exec_()) 26 | -------------------------------------------------------------------------------- /Chapter07/crash_course_qt_for_python/hello_connect_signal_slot.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit, QLabel 3 | from PySide2 import QtCore 4 | 5 | app = QApplication(sys.argv) 6 | 7 | hello_line_edit = QLineEdit() 8 | world_label = QLabel("") 9 | 10 | layout = QVBoxLayout() 11 | layout.addWidget(hello_line_edit) 12 | layout.addWidget(world_label) 13 | 14 | hello_line_edit.connect(QtCore.SIGNAL('textChanged(QString)'), world_label, QtCore.SLOT('setText(QString)')) 15 | #QtCore.QObject.connect(hello_line_edit, QtCore.SIGNAL('textChanged(QString)'), world_label, QtCore.SLOT('setText(QString)')) 16 | 17 | window = QWidget() 18 | window.setLayout(layout) 19 | window.resize(200, 200) 20 | 21 | window.show() 22 | sys.exit(app.exec_()) 23 | -------------------------------------------------------------------------------- /Chapter07/crash_course_qt_for_python/hello_connect_simple.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QLabel 3 | from PySide2 import QtCore 4 | 5 | app = QApplication(sys.argv) 6 | 7 | hello_button = QPushButton("Hello") 8 | world_label = QLabel("Sun") 9 | 10 | layout = QVBoxLayout() 11 | layout.addWidget(hello_button) 12 | layout.addWidget(world_label) 13 | 14 | def set_text_in_world_label(): 15 | world_label.setText("World") 16 | 17 | hello_button.connect(QtCore.SIGNAL('clicked()'), set_text_in_world_label) 18 | 19 | window = QWidget() 20 | window.setLayout(layout) 21 | window.resize(200, 200) 22 | 23 | window.show() 24 | sys.exit(app.exec_()) 25 | -------------------------------------------------------------------------------- /Chapter07/crash_course_qt_for_python/hello_custom_signal_slot.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PySide2 import QtCore 3 | 4 | @QtCore.Slot(str) 5 | def slot_func(param): 6 | print(param) 7 | 8 | class Simple(QtCore.QObject): 9 | signal = QtCore.Signal(str) 10 | 11 | simple = Simple() 12 | simple.signal.connect(slot_func) 13 | 14 | simple.signal.emit("Hello World") 15 | -------------------------------------------------------------------------------- /Chapter07/crash_course_qt_for_python/hello_horizontal_layout.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PySide2.QtWidgets import QApplication, QWidget, QHBoxLayout, QPushButton, QLabel 3 | 4 | app = QApplication(sys.argv) 5 | 6 | hello_button = QPushButton('Hello') 7 | very_label = QLabel('Very Very') 8 | beautiful_button = QPushButton('Beautiful') 9 | world_label = QLabel('World') 10 | 11 | layout = QHBoxLayout() 12 | layout.addWidget(hello_button) 13 | layout.addWidget(very_label) 14 | layout.addWidget(beautiful_button) 15 | layout.addWidget(world_label) 16 | 17 | window = QWidget() 18 | window.setLayout(layout) 19 | window.resize(200, 200) 20 | 21 | window.show() 22 | sys.exit(app.exec_()) 23 | -------------------------------------------------------------------------------- /Chapter07/crash_course_qt_for_python/hello_thread.py: -------------------------------------------------------------------------------- 1 | from PySide2 import QtCore 2 | import time 3 | 4 | class SimpleThread(QtCore.QThread): 5 | 6 | def __init__(self, parent=None): 7 | super(SimpleThread, self).__init__(parent) 8 | 9 | def run(self): 10 | time.sleep(2) # simulating latency in network 11 | print("world") 12 | 13 | 14 | simple_thread = SimpleThread() 15 | simple_thread.start() 16 | 17 | print("hello") 18 | simple_thread.wait() 19 | -------------------------------------------------------------------------------- /Chapter07/crash_course_qt_for_python/hello_varieties.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PySide2.QtWidgets import (QApplication, 3 | QWidget, 4 | QVBoxLayout, 5 | QHBoxLayout, 6 | QGroupBox, 7 | QPushButton, 8 | QLabel, 9 | QSpinBox, 10 | QLineEdit, 11 | QRadioButton, 12 | QComboBox) 13 | 14 | app = QApplication(sys.argv) 15 | 16 | button = QPushButton('Button') 17 | label = QLabel('Label') 18 | spinbox = QSpinBox() 19 | lineedit = QLineEdit() 20 | radio_button1 = QRadioButton('Option 1') 21 | radio_button2 = QRadioButton('Option 2') 22 | radio_button3 = QRadioButton('Option 3') 23 | combo_box = QComboBox() 24 | combo_box.addItems(["Bitcoin", "Ethereum", "Monero", "Ripple"]) 25 | 26 | vlayout = QVBoxLayout() 27 | vlayout.addWidget(button) 28 | vlayout.addWidget(radio_button1) 29 | vlayout.addWidget(radio_button2) 30 | vlayout.addWidget(radio_button3) 31 | vlayout.addWidget(spinbox) 32 | 33 | hlayout = QHBoxLayout() 34 | hlayout.addWidget(lineedit) 35 | hlayout.addWidget(label) 36 | hlayout.addWidget(combo_box) 37 | 38 | top_groupbox = QGroupBox('Top') 39 | top_groupbox.setLayout(vlayout) 40 | 41 | bottom_groupbox = QGroupBox('Bottom') 42 | bottom_groupbox.setLayout(hlayout) 43 | 44 | layout = QVBoxLayout() 45 | layout.addWidget(top_groupbox) 46 | layout.addWidget(bottom_groupbox) 47 | 48 | window = QWidget() 49 | window.setLayout(layout) 50 | 51 | window.show() 52 | sys.exit(app.exec_()) 53 | -------------------------------------------------------------------------------- /Chapter07/crash_course_qt_for_python/hello_vertical_horizontal_layout.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel 3 | 4 | app = QApplication(sys.argv) 5 | 6 | hello_button = QPushButton('Hello') 7 | very_label = QLabel('Very Very') 8 | beautiful_button = QPushButton('Beautiful') 9 | world_label = QLabel('World') 10 | 11 | vertical_hello_button = QPushButton('Hello') 12 | vertical_very_label = QLabel('Very Very') 13 | vertical_beautiful_button = QPushButton('Beautiful') 14 | vertical_world_label = QLabel('World') 15 | 16 | vertical_layout = QVBoxLayout() 17 | vertical_layout.addWidget(vertical_hello_button) 18 | vertical_layout.addWidget(vertical_very_label) 19 | vertical_layout.addWidget(vertical_beautiful_button) 20 | vertical_layout.addWidget(vertical_world_label) 21 | 22 | horizontal_layout = QHBoxLayout() 23 | horizontal_layout.addWidget(hello_button) 24 | horizontal_layout.addWidget(very_label) 25 | horizontal_layout.addLayout(vertical_layout) 26 | horizontal_layout.addWidget(beautiful_button) 27 | horizontal_layout.addWidget(world_label) 28 | 29 | window = QWidget() 30 | window.setLayout(horizontal_layout) 31 | window.resize(200, 200) 32 | 33 | window.show() 34 | sys.exit(app.exec_()) 35 | -------------------------------------------------------------------------------- /Chapter07/crash_course_qt_for_python/hello_vertical_layout.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QLabel 3 | 4 | app = QApplication(sys.argv) 5 | 6 | hello_button = QPushButton('Hello') 7 | very_label = QLabel('Very Very') 8 | beautiful_button = QPushButton('Beautiful') 9 | world_label = QLabel('World') 10 | 11 | layout = QVBoxLayout() 12 | layout.addWidget(hello_button) 13 | layout.addWidget(very_label) 14 | layout.addWidget(beautiful_button) 15 | layout.addWidget(world_label) 16 | 17 | window = QWidget() 18 | window.setLayout(layout) 19 | window.resize(200, 200) 20 | 21 | window.show() 22 | sys.exit(app.exec_()) 23 | -------------------------------------------------------------------------------- /Chapter07/dapp/address.txt: -------------------------------------------------------------------------------- 1 | 0x9Dc44aa8d05c86388E647F954D00CaA858837804 2 | -------------------------------------------------------------------------------- /Chapter07/dapp/fixtures.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3, HTTPProvider 2 | from populus.utils.wait import wait_for_transaction_receipt 3 | 4 | w3 = Web3(HTTPProvider('http://localhost:7545')) 5 | 6 | private_keys = ['dummy', 7 | '59e31694256f71b8d181f47fc67914798c4b96990e835fc1407bf4673ead30e2', 8 | 'ac1e6abbe002699fbef756a2cbc2bf8c03cfac97adee84ce32f198219be94788'] 9 | 10 | false = False 11 | true = True 12 | abi = [ 13 | { 14 | "constant": false, 15 | "gas": 71987, 16 | "inputs": [ 17 | { 18 | "name": "tweet", 19 | "type": "bytes32" 20 | } 21 | ], 22 | "name": "write_a_tweet", 23 | "outputs": [], 24 | "payable": false, 25 | "type": "function" 26 | }, 27 | { 28 | "constant": true, 29 | "gas": 968, 30 | "inputs": [ 31 | { 32 | "name": "arg0", 33 | "type": "address" 34 | }, 35 | { 36 | "name": "arg1", 37 | "type": "int128" 38 | } 39 | ], 40 | "name": "tweets__messages", 41 | "outputs": [ 42 | { 43 | "name": "out", 44 | "type": "bytes32" 45 | } 46 | ], 47 | "payable": false, 48 | "type": "function" 49 | }, 50 | { 51 | "constant": true, 52 | "gas": 787, 53 | "inputs": [ 54 | { 55 | "name": "arg0", 56 | "type": "address" 57 | } 58 | ], 59 | "name": "tweets__index", 60 | "outputs": [ 61 | { 62 | "name": "out", 63 | "type": "int128" 64 | } 65 | ], 66 | "payable": false, 67 | "type": "function" 68 | } 69 | ] 70 | 71 | with open('address.txt', 'r') as f: 72 | address = f.read().rstrip("\n") 73 | 74 | TwitterOnBlockchain = w3.eth.contract(address=address, abi=abi) 75 | 76 | for i in range(1, 3): 77 | for j in range(1, 11): 78 | nonce = w3.eth.getTransactionCount(Web3.toChecksumAddress(w3.eth.accounts[i])) 79 | txn = TwitterOnBlockchain.functions.write_a_tweet(b'Tweet ' + str(j).encode('utf-8')).buildTransaction({ 80 | 'gas': 70000, 81 | 'gasPrice': w3.toWei('1', 'gwei'), 82 | 'nonce': nonce 83 | }) 84 | 85 | signed = w3.eth.account.signTransaction(txn, private_key=private_keys[i]) 86 | txhash = w3.eth.sendRawTransaction(signed.rawTransaction) 87 | wait_for_transaction_receipt(w3, txhash) 88 | -------------------------------------------------------------------------------- /Chapter07/twitter_on_blockchain/build/contracts.json: -------------------------------------------------------------------------------- 1 | { 2 | "TwitterOnBlockchain": { 3 | "abi": [ 4 | { 5 | "constant": false, 6 | "gas": 71987, 7 | "inputs": [ 8 | { 9 | "name": "tweet", 10 | "type": "bytes32" 11 | } 12 | ], 13 | "name": "write_a_tweet", 14 | "outputs": [], 15 | "payable": false, 16 | "type": "function" 17 | }, 18 | { 19 | "constant": true, 20 | "gas": 968, 21 | "inputs": [ 22 | { 23 | "name": "arg0", 24 | "type": "address" 25 | }, 26 | { 27 | "name": "arg1", 28 | "type": "int128" 29 | } 30 | ], 31 | "name": "tweets__messages", 32 | "outputs": [ 33 | { 34 | "name": "out", 35 | "type": "bytes32" 36 | } 37 | ], 38 | "payable": false, 39 | "type": "function" 40 | }, 41 | { 42 | "constant": true, 43 | "gas": 787, 44 | "inputs": [ 45 | { 46 | "name": "arg0", 47 | "type": "address" 48 | } 49 | ], 50 | "name": "tweets__index", 51 | "outputs": [ 52 | { 53 | "name": "out", 54 | "type": "int128" 55 | } 56 | ], 57 | "payable": false, 58 | "type": "function" 59 | } 60 | ], 61 | "bytecode": "0x61026556600035601c52740100000000000000000000000000000000000000006020526f7fffffffffffffffffffffffffffffff6040527fffffffffffffffffffffffffffffffff8000000000000000000000000000000060605274012a05f1fffffffffffffffffffffffffdabf41c006080527ffffffffffffffffffffffffed5fa0e000000000000000000000000000000000060a05263ac4cc57b600051141561016b57602060046101403734156100b457600080fd5b600a60003360e05260c052604060c02060c052602060c02054126100d757600080fd5b60003360e05260c052604060c02060c052602060c02054610160526101405161016051600a811061010757600080fd5b600160003360e05260c052604060c02060c052602060c0200160c052602060c020015560003360e05260c052604060c02060c052602060c02060605160018254018060405190131561015857600080fd5b809190121561016657600080fd5b815550005b632bd5f9176000511415610204576040600461014037341561018c57600080fd5b600435602051811061019d57600080fd5b50606051602435806040519013156101b457600080fd5b80919012156101c257600080fd5b5061016051600a81106101d457600080fd5b600160006101405160e05260c052604060c02060c052602060c0200160c052602060c020015460005260206000f3005b6372241d6e600051141561025b576020600461014037341561022557600080fd5b600435602051811061023657600080fd5b5060006101405160e05260c052604060c02060c052602060c0205460005260206000f3005b60006000fd5b61000461026503610004600039610004610265036000f3", 62 | "bytecode_runtime": "0x600035601c52740100000000000000000000000000000000000000006020526f7fffffffffffffffffffffffffffffff6040527fffffffffffffffffffffffffffffffff8000000000000000000000000000000060605274012a05f1fffffffffffffffffffffffffdabf41c006080527ffffffffffffffffffffffffed5fa0e000000000000000000000000000000000060a05263ac4cc57b600051141561016b57602060046101403734156100b457600080fd5b600a60003360e05260c052604060c02060c052602060c02054126100d757600080fd5b60003360e05260c052604060c02060c052602060c02054610160526101405161016051600a811061010757600080fd5b600160003360e05260c052604060c02060c052602060c0200160c052602060c020015560003360e05260c052604060c02060c052602060c02060605160018254018060405190131561015857600080fd5b809190121561016657600080fd5b815550005b632bd5f9176000511415610204576040600461014037341561018c57600080fd5b600435602051811061019d57600080fd5b50606051602435806040519013156101b457600080fd5b80919012156101c257600080fd5b5061016051600a81106101d457600080fd5b600160006101405160e05260c052604060c02060c052602060c0200160c052602060c020015460005260206000f3005b6372241d6e600051141561025b576020600461014037341561022557600080fd5b600435602051811061023657600080fd5b5060006101405160e05260c052604060c02060c052602060c0205460005260206000f3005b60006000fd", 63 | "direct_dependencies": [], 64 | "full_dependencies": [], 65 | "linkrefs": [], 66 | "linkrefs_runtime": [], 67 | "name": "TwitterOnBlockchain", 68 | "ordered_full_dependencies": [], 69 | "source_path": "contracts/TwitterOnBlockchain.vy" 70 | } 71 | } -------------------------------------------------------------------------------- /Chapter07/twitter_on_blockchain/contracts/TwitterOnBlockchain.vy: -------------------------------------------------------------------------------- 1 | struct Tweet: 2 | messages: bytes32[10] 3 | index: int128 4 | 5 | tweets: public(map(address, Tweet)) 6 | 7 | @public 8 | def write_a_tweet(tweet: bytes32): 9 | assert self.tweets[msg.sender].index < 10 10 | 11 | index: int128 = self.tweets[msg.sender].index 12 | self.tweets[msg.sender].messages[index] = tweet 13 | self.tweets[msg.sender].index += 1 14 | -------------------------------------------------------------------------------- /Chapter07/twitter_on_blockchain/tests/test_twitter_on_blockchain.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import eth_tester 3 | 4 | 5 | def test_initial_condition(web3, chain): 6 | twitter_on_blockchain, _ = chain.provider.get_or_deploy_contract('TwitterOnBlockchain') 7 | 8 | assert twitter_on_blockchain.functions.tweets__index(web3.eth.coinbase).call() == 0 9 | 10 | def test_write_a_tweet(web3, chain): 11 | twitter_on_blockchain, _ = chain.provider.get_or_deploy_contract('TwitterOnBlockchain') 12 | 13 | tweet = b'My first tweet ever!' 14 | set_txn_hash = twitter_on_blockchain.functions.write_a_tweet(tweet).transact() 15 | chain.wait.for_receipt(set_txn_hash) 16 | assert twitter_on_blockchain.functions.tweets__index(web3.eth.coinbase).call() == 1 17 | assert twitter_on_blockchain.functions.tweets__messages(web3.eth.coinbase,0).call()[:len(tweet)] == tweet 18 | 19 | tweet2 = b'I feel sad today.' 20 | set_txn_hash = twitter_on_blockchain.functions.write_a_tweet(tweet2).transact() 21 | chain.wait.for_receipt(set_txn_hash) 22 | assert twitter_on_blockchain.functions.tweets__index(web3.eth.coinbase).call() == 2 23 | assert twitter_on_blockchain.functions.tweets__messages(web3.eth.coinbase,0).call()[:len(tweet)] == tweet 24 | assert twitter_on_blockchain.functions.tweets__messages(web3.eth.coinbase,1).call()[:len(tweet2)] == tweet2 25 | -------------------------------------------------------------------------------- /Chapter08/token_project/chains/localblock/genesis.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 4 | "coinbase": "0x60d31d5e29a9c74448855b97017322972d4a798e", 5 | "extraData": "0x686f727365", 6 | "config": { 7 | "daoForkBlock": 0, 8 | "daoForSupport": true, 9 | "homesteadBlock": 0 10 | }, 11 | "timestamp": "0x0", 12 | "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", 13 | "nonce": "0xdeadbeefdeadbeef", 14 | "alloc": { 15 | "0x60d31d5e29a9c74448855b97017322972d4a798e":{ 16 | "balance": "1000000000000000000000000000000" 17 | } 18 | }, 19 | "gasLimit": "0x47d5cc", 20 | "difficulty": "0x01" 21 | } 22 | -------------------------------------------------------------------------------- /Chapter08/token_project/chains/localblock/init_chain.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | geth --rpc --rpcaddr 127.0.0.1 --rpcport 8545 --rpcapi admin,debug,eth,miner,net,personal,shh,txpool,web3,ws --ws --wsaddr 127.0.0.1 --wsport 8546 --wsapi admin,debug,eth,miner,net,personal,shh,txpool,web3,ws --datadir /home/arjuna/Documents/WritingBook/hands-on-blockchain-for-python-developers/chapter_08/token_project/chains/localblock/chain_data --maxpeers 0 --networkid 1234 --port 30303 --ipcpath /tmp/tmpjj4jkwss/geth.ipc --nodiscover --mine --minerthreads 1 init /home/arjuna/Documents/WritingBook/hands-on-blockchain-for-python-developers/chapter_08/token_project/chains/localblock/genesis.json -------------------------------------------------------------------------------- /Chapter08/token_project/chains/localblock/password: -------------------------------------------------------------------------------- 1 | this-is-not-a-secure-password 2 | -------------------------------------------------------------------------------- /Chapter08/token_project/chains/localblock/run_chain.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | geth --rpc --rpcaddr 127.0.0.1 --rpcport 8545 --rpcapi admin,debug,eth,miner,net,personal,shh,txpool,web3,ws --ws --wsaddr 127.0.0.1 --wsport 8546 --wsapi admin,debug,eth,miner,net,personal,shh,txpool,web3,ws --datadir /home/arjuna/Documents/WritingBook/hands-on-blockchain-for-python-developers/chapter_08/token_project/chains/localblock/chain_data --maxpeers 0 --networkid 1234 --port 30303 --ipcpath /tmp/tmpjj4jkwss/geth.ipc --unlock 0x60d31d5e29a9c74448855b97017322972d4a798e --password /home/arjuna/Documents/WritingBook/hands-on-blockchain-for-python-developers/chapter_08/token_project/chains/localblock/password --nodiscover --mine --minerthreads 1 --ipcpath /tmp/geth.ipc 3 | -------------------------------------------------------------------------------- /Chapter08/token_project/contracts/CrowdSaleToken.vy: -------------------------------------------------------------------------------- 1 | Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256}) 2 | Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256}) 3 | Payment: event({_buyer: indexed(address), _value: uint256(wei)}) 4 | 5 | name: public(bytes[10]) 6 | symbol: public(bytes[3]) 7 | totalSupply: public(uint256) 8 | decimals: public(uint256) 9 | balances: map(address, uint256) 10 | ethBalances: public(map(address, uint256(wei))) 11 | allowed: map(address, map(address, uint256)) 12 | 13 | beneficiary: public(address) 14 | minFundingGoal: public(uint256(wei)) 15 | maxFundingGoal: public(uint256(wei)) 16 | amountRaised: public(uint256(wei)) 17 | deadline: public(timestamp) 18 | price: public(uint256(wei)) 19 | fundingGoalReached: public(bool) 20 | crowdsaleClosed: public(bool) 21 | 22 | @public 23 | def __init__(): 24 | _initialSupply: uint256 = 100 25 | _decimals: uint256 = 2 26 | self.totalSupply = _initialSupply * 10 ** _decimals 27 | self.name = 'Haha Coin' 28 | self.symbol = 'HAH' 29 | self.decimals = _decimals 30 | self.beneficiary = msg.sender 31 | self.balances[msg.sender] = self.totalSupply 32 | self.minFundingGoal = as_wei_value(30, "ether") 33 | self.maxFundingGoal = as_wei_value(50, "ether") 34 | self.amountRaised = 0 35 | self.deadline = block.timestamp + 3600 * 24 * 100 # 100 days 36 | self.price = as_wei_value(1, "ether") / 100 37 | self.fundingGoalReached = False 38 | self.crowdsaleClosed = False 39 | 40 | @public 41 | @payable 42 | def __default__(): 43 | assert msg.sender != self.beneficiary 44 | assert self.crowdsaleClosed == False 45 | assert self.amountRaised + msg.value < self.maxFundingGoal 46 | assert msg.value >= as_wei_value(0.01, "ether") 47 | self.ethBalances[msg.sender] += msg.value 48 | self.amountRaised += msg.value 49 | tokenAmount: uint256 = msg.value / self.price 50 | self.balances[msg.sender] += tokenAmount 51 | self.balances[self.beneficiary] -= tokenAmount 52 | log.Payment(msg.sender, msg.value) 53 | 54 | @public 55 | def checkGoalReached(): 56 | assert block.timestamp > self.deadline 57 | if self.amountRaised >= self.minFundingGoal: 58 | self.fundingGoalReached = True 59 | self.crowdsaleClosed = True 60 | 61 | @public 62 | def safeWithdrawal(): 63 | assert self.crowdsaleClosed == True 64 | if self.fundingGoalReached == False: 65 | if msg.sender != self.beneficiary: 66 | if self.ethBalances[msg.sender] > 0: 67 | self.ethBalances[msg.sender] = 0 68 | self.balances[self.beneficiary] += self.balances[msg.sender] 69 | self.balances[msg.sender] = 0 70 | send(msg.sender, self.ethBalances[msg.sender]) 71 | if self.fundingGoalReached == True: 72 | if msg.sender == self.beneficiary: 73 | if self.balance > 0: 74 | send(msg.sender, self.balance) 75 | 76 | @public 77 | @constant 78 | def balanceOf(_owner: address) -> uint256: 79 | return self.balances[_owner] 80 | 81 | @public 82 | def transfer(_to: address, _amount: uint256) -> bool: 83 | assert self.crowdsaleClosed == True 84 | assert self.balances[msg.sender] >= _amount 85 | self.balances[msg.sender] -= _amount 86 | self.balances[_to] += _amount 87 | log.Transfer(msg.sender, _to, _amount) 88 | 89 | return True 90 | 91 | @public 92 | def transferFrom(_from: address, _to: address, _value: uint256) -> bool: 93 | assert self.crowdsaleClosed == True 94 | assert _value <= self.allowed[_from][msg.sender] 95 | assert _value <= self.balances[_from] 96 | 97 | self.balances[_from] -= _value 98 | self.allowed[_from][msg.sender] -= _value 99 | self.balances[_to] += _value 100 | log.Transfer(_from, _to, _value) 101 | 102 | return True 103 | 104 | @public 105 | def approve(_spender: address, _amount: uint256) -> bool: 106 | assert self.crowdsaleClosed == True 107 | self.allowed[msg.sender][_spender] = _amount 108 | log.Approval(msg.sender, _spender, _amount) 109 | 110 | return True 111 | 112 | @public 113 | @constant 114 | def allowance(_owner: address, _spender: address) -> uint256: 115 | return self.allowed[_owner][_spender] 116 | -------------------------------------------------------------------------------- /Chapter08/token_project/contracts/ERC20Token.vy: -------------------------------------------------------------------------------- 1 | Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256}) 2 | Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256}) 3 | 4 | name: public(bytes[10]) 5 | symbol: public(bytes[3]) 6 | totalSupply: public(uint256) 7 | decimals: public(uint256) 8 | balances: map(address, uint256) 9 | allowed: map(address, map(address, uint256)) 10 | 11 | @public 12 | def __init__(): 13 | _initialSupply: uint256 = 1000 14 | _decimals: uint256 = 3 15 | self.totalSupply = _initialSupply * 10 ** _decimals 16 | self.balances[msg.sender] = self.totalSupply 17 | self.name = 'Haha Coin' 18 | self.symbol = 'HAH' 19 | self.decimals = _decimals 20 | log.Transfer(ZERO_ADDRESS, msg.sender, self.totalSupply) 21 | 22 | @public 23 | @constant 24 | def balanceOf(_owner: address) -> uint256: 25 | return self.balances[_owner] 26 | 27 | @public 28 | def transfer(_to: address, _amount: uint256) -> bool: 29 | assert self.balances[msg.sender] >= _amount 30 | self.balances[msg.sender] -= _amount 31 | self.balances[_to] += _amount 32 | log.Transfer(msg.sender, _to, _amount) 33 | 34 | return True 35 | 36 | @public 37 | def transferFrom(_from: address, _to: address, _value: uint256) -> bool: 38 | assert _value <= self.allowed[_from][msg.sender] 39 | assert _value <= self.balances[_from] 40 | 41 | self.balances[_from] -= _value 42 | self.allowed[_from][msg.sender] -= _value 43 | self.balances[_to] += _value 44 | log.Transfer(_from, _to, _value) 45 | 46 | return True 47 | 48 | @public 49 | def approve(_spender: address, _amount: uint256) -> bool: 50 | self.allowed[msg.sender][_spender] = _amount 51 | log.Approval(msg.sender, _spender, _amount) 52 | 53 | return True 54 | 55 | @public 56 | @constant 57 | def allowance(_owner: address, _spender: address) -> uint256: 58 | return self.allowed[_owner][_spender] 59 | -------------------------------------------------------------------------------- /Chapter08/token_project/contracts/SimpleToken.vy: -------------------------------------------------------------------------------- 1 | balances: public(map(address, uint256)) 2 | 3 | 4 | @public 5 | def __init__(): 6 | self.balances[msg.sender] = 10000 7 | 8 | 9 | @public 10 | def transfer(_to: address, _amount: uint256) -> bool: 11 | assert self.balances[msg.sender] >= _amount 12 | 13 | self.balances[msg.sender] -= _amount 14 | self.balances[_to] += _amount 15 | 16 | return True 17 | -------------------------------------------------------------------------------- /Chapter08/token_project/contracts/SimpleTokenMint.vy: -------------------------------------------------------------------------------- 1 | balances: public(map(address, uint256)) 2 | owner: address 3 | 4 | 5 | @public 6 | def __init__(): 7 | self.balances[msg.sender] = 10000 8 | self.owner = msg.sender 9 | 10 | 11 | @public 12 | def transfer(_to: address, _amount: uint256) -> bool: 13 | assert self.balances[msg.sender] >= _amount 14 | 15 | self.balances[msg.sender] -= _amount 16 | self.balances[_to] += _amount 17 | 18 | return True 19 | 20 | @public 21 | def mint(_new_supply: uint256): 22 | assert msg.sender == self.owner 23 | self.balances[msg.sender] = _new_supply 24 | -------------------------------------------------------------------------------- /Chapter08/token_project/contracts/StableCoin.vy: -------------------------------------------------------------------------------- 1 | Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256}) 2 | Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256}) 3 | Freeze: event({_account: indexed(address), _freeze: bool}) 4 | 5 | name: public(bytes[10]) 6 | symbol: public(bytes[3]) 7 | totalSupply: public(uint256) 8 | decimals: public(uint256) 9 | balances: map(address, uint256) 10 | allowed: map(address, map(address, uint256)) 11 | frozenBalances: public(map(address, bool)) 12 | owner: public(address) 13 | 14 | @public 15 | def __init__(): 16 | _initialSupply: uint256 = 1000 17 | _decimals: uint256 = 3 18 | self.totalSupply = _initialSupply * 10 ** _decimals 19 | self.balances[msg.sender] = self.totalSupply 20 | self.name = 'Haha Coin' 21 | self.symbol = 'HAH' 22 | self.decimals = _decimals 23 | self.owner = msg.sender 24 | log.Transfer(ZERO_ADDRESS, msg.sender, self.totalSupply) 25 | 26 | @public 27 | def freezeBalance(_target: address, _freeze: bool) -> bool: 28 | assert msg.sender == self.owner 29 | self.frozenBalances[_target] = _freeze 30 | log.Freeze(_target, _freeze) 31 | 32 | return True 33 | 34 | @public 35 | def mintToken(_mintedAmount: uint256) -> bool: 36 | assert msg.sender == self.owner 37 | self.totalSupply += _mintedAmount 38 | self.balances[msg.sender] += _mintedAmount 39 | log.Transfer(ZERO_ADDRESS, msg.sender, _mintedAmount) 40 | 41 | return True 42 | 43 | @public 44 | def burn(_burntAmount: uint256) -> bool: 45 | assert msg.sender == self.owner 46 | assert self.balances[msg.sender] >= _burntAmount 47 | self.totalSupply -= _burntAmount 48 | self.balances[msg.sender] -= _burntAmount 49 | log.Transfer(msg.sender, ZERO_ADDRESS, _burntAmount) 50 | 51 | return True 52 | 53 | @public 54 | @constant 55 | def balanceOf(_owner: address) -> uint256: 56 | return self.balances[_owner] 57 | 58 | @public 59 | def transfer(_to: address, _amount: uint256) -> bool: 60 | assert self.balances[msg.sender] >= _amount 61 | assert self.frozenBalances[msg.sender] == False 62 | self.balances[msg.sender] -= _amount 63 | self.balances[_to] += _amount 64 | log.Transfer(msg.sender, _to, _amount) 65 | 66 | return True 67 | 68 | @public 69 | def transferFrom(_from: address, _to: address, _value: uint256) -> bool: 70 | assert _value <= self.allowed[_from][msg.sender] 71 | assert _value <= self.balances[_from] 72 | assert self.frozenBalances[msg.sender] == False 73 | 74 | self.balances[_from] -= _value 75 | self.allowed[_from][msg.sender] -= _value 76 | self.balances[_to] += _value 77 | log.Transfer(_from, _to, _value) 78 | 79 | return True 80 | 81 | @public 82 | def approve(_spender: address, _amount: uint256) -> bool: 83 | self.allowed[msg.sender][_spender] = _amount 84 | log.Approval(msg.sender, _spender, _amount) 85 | 86 | return True 87 | 88 | @public 89 | @constant 90 | def allowance(_owner: address, _spender: address) -> uint256: 91 | return self.allowed[_owner][_spender] 92 | -------------------------------------------------------------------------------- /Chapter08/token_project/tests/test_erc20_token.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import eth_tester 3 | 4 | 5 | def test_balance(web3, chain): 6 | erc20_token, _ = chain.provider.get_or_deploy_contract('ERC20Token') 7 | 8 | token_name = erc20_token.functions.name().call() 9 | token_symbol = erc20_token.functions.symbol().call() 10 | decimals = erc20_token.functions.decimals().call() 11 | total_supply = erc20_token.functions.totalSupply().call() 12 | balance = erc20_token.functions.balanceOf(web3.eth.coinbase).call() 13 | 14 | assert token_name == b"Haha Coin" 15 | assert token_symbol == b"HAH" 16 | assert decimals == 3 17 | assert total_supply == 1000000 18 | assert balance == 1000000 19 | 20 | def test_transfer(web3, chain): 21 | erc20_token, _ = chain.provider.get_or_deploy_contract('ERC20Token') 22 | 23 | t = eth_tester.EthereumTester() 24 | account2 = t.get_accounts()[1] 25 | 26 | old_balance = erc20_token.functions.balanceOf(account2).call() 27 | assert old_balance == 0 28 | 29 | set_txn_hash = erc20_token.functions.transfer(account2, 10).transact({'from': web3.eth.coinbase}) 30 | chain.wait.for_receipt(set_txn_hash) 31 | 32 | sender_new_balance = erc20_token.functions.balanceOf(web3.eth.coinbase).call() 33 | assert sender_new_balance == 999990 34 | destination_new_balance = erc20_token.functions.balanceOf(account2).call() 35 | assert destination_new_balance == 10 36 | 37 | def test_fail_transfer(web3, chain): 38 | erc20_token, _ = chain.provider.get_or_deploy_contract('ERC20Token') 39 | 40 | t = eth_tester.EthereumTester() 41 | account2 = t.get_accounts()[1] 42 | 43 | with pytest.raises(eth_tester.exceptions.TransactionFailed): 44 | erc20_token.functions.transfer(web3.eth.coinbase, 10).transact({'from': account2}) 45 | 46 | def test_approve(web3, chain): 47 | erc20_token, _ = chain.provider.get_or_deploy_contract('ERC20Token') 48 | 49 | t = eth_tester.EthereumTester() 50 | account2 = t.get_accounts()[1] 51 | 52 | allowance = erc20_token.functions.allowance(web3.eth.coinbase, account2).call() 53 | assert allowance == 0 54 | 55 | set_txn_hash = erc20_token.functions.approve(account2, 100).transact({'from': web3.eth.coinbase}) 56 | chain.wait.for_receipt(set_txn_hash) 57 | 58 | allowance = erc20_token.functions.allowance(web3.eth.coinbase, account2).call() 59 | assert allowance == 100 60 | 61 | def test_transferFrom(web3, chain): 62 | erc20_token, _ = chain.provider.get_or_deploy_contract('ERC20Token') 63 | 64 | t = eth_tester.EthereumTester() 65 | account2 = t.get_accounts()[1] 66 | account3 = t.get_accounts()[2] 67 | 68 | with pytest.raises(eth_tester.exceptions.TransactionFailed): 69 | erc20_token.functions.transferFrom(web3.eth.coinbase, account3, 10).transact({'from': account2}) 70 | 71 | set_txn_hash = erc20_token.functions.approve(account2, 100).transact({'from': web3.eth.coinbase}) 72 | chain.wait.for_receipt(set_txn_hash) 73 | 74 | set_txn_hash = erc20_token.functions.transferFrom(web3.eth.coinbase, account3, 10).transact({'from': account2}) 75 | chain.wait.for_receipt(set_txn_hash) 76 | 77 | balance_account1 = erc20_token.functions.balanceOf(web3.eth.coinbase).call() 78 | balance_account2 = erc20_token.functions.balanceOf(account2).call() 79 | balance_account3 = erc20_token.functions.balanceOf(account3).call() 80 | allowance = erc20_token.functions.allowance(web3.eth.coinbase, account2).call() 81 | 82 | assert balance_account1 == 999990 83 | assert balance_account2 == 0 84 | assert balance_account3 == 10 85 | assert allowance == 90 86 | -------------------------------------------------------------------------------- /Chapter08/token_project/tests/test_simple_token.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import eth_tester 3 | 4 | 5 | def test_balance(web3, chain): 6 | simple_token, _ = chain.provider.get_or_deploy_contract('SimpleToken') 7 | 8 | balance = simple_token.functions.balances(web3.eth.coinbase).call() 9 | assert balance == 10000 10 | 11 | def test_transfer(web3, chain): 12 | simple_token, _ = chain.provider.get_or_deploy_contract('SimpleToken') 13 | 14 | t = eth_tester.EthereumTester() 15 | account2 = t.get_accounts()[1] 16 | 17 | old_balance = simple_token.functions.balances(account2).call() 18 | assert old_balance == 0 19 | 20 | set_txn_hash = simple_token.functions.transfer(account2, 10).transact({'from': web3.eth.coinbase}) 21 | chain.wait.for_receipt(set_txn_hash) 22 | 23 | sender_new_balance = simple_token.functions.balances(web3.eth.coinbase).call() 24 | assert sender_new_balance == 9990 25 | destination_new_balance = simple_token.functions.balances(account2).call() 26 | assert destination_new_balance == 10 27 | 28 | def test_fail_transfer(web3, chain): 29 | simple_token, _ = chain.provider.get_or_deploy_contract('SimpleToken') 30 | 31 | t = eth_tester.EthereumTester() 32 | account2 = t.get_accounts()[1] 33 | 34 | with pytest.raises(eth_tester.exceptions.TransactionFailed): 35 | simple_token.functions.transfer(web3.eth.coinbase, 10).transact({'from': account2}) 36 | -------------------------------------------------------------------------------- /Chapter08/token_project/tests/test_stable_token.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import eth_tester 3 | import time 4 | 5 | def test_initialization(web3, chain): 6 | stable_coin, _ = chain.provider.get_or_deploy_contract('StableCoin') 7 | 8 | token_name = stable_coin.functions.name().call() 9 | token_symbol = stable_coin.functions.symbol().call() 10 | decimals = stable_coin.functions.decimals().call() 11 | total_supply = stable_coin.functions.totalSupply().call() 12 | owner = stable_coin.functions.owner().call() 13 | 14 | assert token_name == b"Haha Coin" 15 | assert token_symbol == b"HAH" 16 | assert decimals == 3 17 | assert total_supply == 1000000 18 | assert owner == web3.eth.coinbase 19 | 20 | def test_freeze_balance(web3, chain): 21 | stable_coin, _ = chain.provider.get_or_deploy_contract('StableCoin') 22 | 23 | t = eth_tester.EthereumTester() 24 | account2 = t.get_accounts()[1] 25 | account3 = t.get_accounts()[2] 26 | 27 | set_txn_hash = stable_coin.functions.transfer(account2, 10).transact() 28 | chain.wait.for_receipt(set_txn_hash) 29 | 30 | set_txn_hash = stable_coin.functions.transfer(account3, 1).transact({'from': account2}) 31 | chain.wait.for_receipt(set_txn_hash) 32 | 33 | set_txn_hash = stable_coin.functions.freezeBalance(account2, True).transact() 34 | chain.wait.for_receipt(set_txn_hash) 35 | 36 | with pytest.raises(eth_tester.exceptions.TransactionFailed): 37 | stable_coin.functions.transfer(account3, 1).transact({'from': account2}) 38 | 39 | set_txn_hash = stable_coin.functions.freezeBalance(account2, False).transact() 40 | chain.wait.for_receipt(set_txn_hash) 41 | 42 | set_txn_hash = stable_coin.functions.transfer(account3, 1).transact({'from': account2}) 43 | chain.wait.for_receipt(set_txn_hash) 44 | 45 | def test_mint_token(web3, chain): 46 | stable_coin, _ = chain.provider.get_or_deploy_contract('StableCoin') 47 | 48 | set_txn_hash = stable_coin.functions.mintToken(100).transact() 49 | chain.wait.for_receipt(set_txn_hash) 50 | 51 | new_total_supply = stable_coin.functions.totalSupply().call() 52 | assert new_total_supply == 1000100 53 | 54 | def test_burn_token(web3, chain): 55 | stable_coin, _ = chain.provider.get_or_deploy_contract('StableCoin') 56 | 57 | set_txn_hash = stable_coin.functions.burn(100).transact() 58 | chain.wait.for_receipt(set_txn_hash) 59 | 60 | new_total_supply = stable_coin.functions.totalSupply().call() 61 | assert new_total_supply == 999900 62 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/add_stretch.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtWidgets import QFrame, QLabel, QWidget, QApplication, QPushButton, QHBoxLayout, QVBoxLayout, QSizePolicy, QSizePolicy 2 | from PySide2.QtCore import Qt 3 | import sys 4 | 5 | 6 | class AddStretch(QWidget): 7 | 8 | def __init__(self): 9 | super(AddStretch, self).__init__() 10 | 11 | label1 = QLabel("label 1") 12 | label1.setFrameStyle(QFrame.Box) 13 | label2 = QLabel("label 2") 14 | label2.setFrameStyle(QFrame.Box) 15 | label3 = QLabel("label 3") 16 | label3.setFrameStyle(QFrame.Box) 17 | label4 = QLabel("label 4") 18 | label4.setFrameStyle(QFrame.Box) 19 | label5 = QLabel("label 5") 20 | label5.setFrameStyle(QFrame.Box) 21 | label6 = QLabel("label 6") 22 | label6.setFrameStyle(QFrame.Box) 23 | label7 = QLabel("label 7") 24 | label7.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) 25 | label7.setFrameStyle(QFrame.Box) 26 | label8 = QLabel("label 8") 27 | label8.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) 28 | label8.setFrameStyle(QFrame.Box) 29 | 30 | layout = QHBoxLayout() 31 | 32 | v_layout1 = QVBoxLayout() 33 | v_layout1.addWidget(label1) 34 | v_layout1.addWidget(label2) 35 | 36 | v_layout2 = QVBoxLayout() 37 | v_layout2.addWidget(label3) 38 | v_layout2.addWidget(label4) 39 | v_layout2.addStretch() 40 | 41 | v_layout3 = QVBoxLayout() 42 | v_layout3.addStretch() 43 | v_layout3.addWidget(label5) 44 | v_layout3.addWidget(label6) 45 | 46 | v_layout4 = QVBoxLayout() 47 | v_layout4.addWidget(label7) 48 | v_layout4.addWidget(label8) 49 | 50 | layout.addLayout(v_layout1) 51 | layout.addLayout(v_layout2) 52 | layout.addLayout(v_layout3) 53 | layout.addLayout(v_layout4) 54 | 55 | self.setLayout(layout) 56 | 57 | 58 | if __name__ == "__main__": 59 | 60 | app = QApplication(sys.argv) 61 | widget = AddStretch() 62 | widget.resize(500, 500) 63 | widget.show() 64 | sys.exit(app.exec_()) 65 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/button_and_dialog.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtWidgets import QWidget, QApplication, QLabel, QPushButton, QVBoxLayout, QInputDialog, QLineEdit 2 | from PySide2.QtCore import Qt 3 | import sys 4 | 5 | 6 | class ButtonAndDialog(QWidget): 7 | 8 | def __init__(self): 9 | super(ButtonAndDialog, self).__init__() 10 | 11 | self.button = QPushButton("button") 12 | self.button.clicked.connect(self.buttonClicked) 13 | 14 | self.label = QLabel("") 15 | 16 | layout = QVBoxLayout() 17 | layout.addWidget(self.button) 18 | layout.addWidget(self.label) 19 | 20 | self.setLayout(layout) 21 | 22 | def buttonClicked(self): 23 | new_text, ok = QInputDialog.getText(self, "Create A Text", 24 | "New Text:", QLineEdit.Normal) 25 | if ok and new_text != '': 26 | self.label.setText(new_text) 27 | 28 | 29 | if __name__ == "__main__": 30 | 31 | app = QApplication(sys.argv) 32 | button_and_dialog = ButtonAndDialog() 33 | button_and_dialog.show() 34 | sys.exit(app.exec_()) 35 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/button_and_label.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtWidgets import QWidget, QApplication, QLabel, QPushButton, QVBoxLayout 2 | from PySide2.QtCore import Qt 3 | import sys 4 | 5 | 6 | class ButtonAndLabel(QWidget): 7 | 8 | def __init__(self): 9 | super(ButtonAndLabel, self).__init__() 10 | 11 | self.button = QPushButton("button") 12 | self.button.clicked.connect(self.buttonClicked) 13 | 14 | self.label = QLabel("label: before clicked") 15 | 16 | layout = QVBoxLayout() 17 | layout.addWidget(self.button) 18 | layout.addWidget(self.label) 19 | 20 | self.setLayout(layout) 21 | 22 | def buttonClicked(self): 23 | self.label.setText("label: after clicked") 24 | 25 | 26 | if __name__ == "__main__": 27 | 28 | app = QApplication(sys.argv) 29 | button_and_label = ButtonAndLabel() 30 | button_and_label.show() 31 | sys.exit(app.exec_()) 32 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/button_and_list.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtWidgets import QWidget, QApplication, QLabel, QPushButton, QVBoxLayout 2 | from PySide2.QtCore import Qt 3 | import sys 4 | 5 | 6 | class ButtonAndList(QWidget): 7 | 8 | index = 0 9 | 10 | def __init__(self): 11 | super(ButtonAndList, self).__init__() 12 | 13 | self.button = QPushButton("button") 14 | self.button.clicked.connect(self.buttonClicked) 15 | 16 | self.v_layout = QVBoxLayout() 17 | 18 | layout = QVBoxLayout() 19 | layout.addWidget(self.button) 20 | layout.addLayout(self.v_layout) 21 | 22 | self.setLayout(layout) 23 | 24 | def buttonClicked(self): 25 | self.index += 1 26 | label = QLabel(str(self.index)) 27 | self.v_layout.addWidget(label) 28 | 29 | 30 | if __name__ == "__main__": 31 | 32 | app = QApplication(sys.argv) 33 | button_and_list = ButtonAndList() 34 | button_and_list.show() 35 | sys.exit(app.exec_()) 36 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/button_and_long_process.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtWidgets import QWidget, QApplication, QLabel, QPushButton, QVBoxLayout 2 | from PySide2.QtCore import Qt, QThread, Signal 3 | import sys 4 | from time import sleep 5 | 6 | 7 | class LongProcessThread(QThread): 8 | 9 | transaction = Signal() 10 | 11 | def __init__(self, parent=None): 12 | super(LongProcessThread, self).__init__(parent) 13 | 14 | def run(self): 15 | sleep(3) 16 | self.transaction.emit() 17 | 18 | 19 | class ButtonAndLongProcess(QWidget): 20 | 21 | def __init__(self): 22 | super(ButtonAndLongProcess, self).__init__() 23 | 24 | self.button = QPushButton("button") 25 | self.button.clicked.connect(self.buttonClicked) 26 | 27 | self.label = QLabel("label: before clicked") 28 | 29 | layout = QVBoxLayout() 30 | layout.addWidget(self.button) 31 | layout.addWidget(self.label) 32 | 33 | self.setLayout(layout) 34 | 35 | self.long_process_thread = LongProcessThread() 36 | self.long_process_thread.transaction.connect(self.afterLongProcess) 37 | 38 | def afterLongProcess(self): 39 | self.label.setText("label: after clicked") 40 | 41 | def buttonClicked(self): 42 | self.long_process_thread.start() 43 | 44 | 45 | if __name__ == "__main__": 46 | 47 | app = QApplication(sys.argv) 48 | button_and_long_process = ButtonAndLongProcess() 49 | button_and_long_process.show() 50 | sys.exit(app.exec_()) 51 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/button_with_sizepolicy.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtWidgets import QWidget, QApplication, QPushButton, QVBoxLayout, QSizePolicy 2 | from PySide2.QtCore import Qt 3 | import sys 4 | 5 | 6 | class ButtonWithSizePolicy(QWidget): 7 | 8 | def __init__(self): 9 | super(ButtonWithSizePolicy, self).__init__() 10 | 11 | button1 = QPushButton("button default") 12 | button2 = QPushButton("button maximum") 13 | button2.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) 14 | button3 = QPushButton("button preferred") 15 | button3.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) 16 | button4 = QPushButton("button expanding") 17 | button4.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 18 | button5 = QPushButton("button minimum") 19 | button5.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) 20 | button6 = QPushButton("button minimum expanding") 21 | button6.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) 22 | 23 | layout = QVBoxLayout() 24 | layout.addWidget(button1) 25 | layout.addWidget(button2) 26 | layout.addWidget(button3) 27 | layout.addWidget(button4) 28 | layout.addWidget(button5) 29 | layout.addWidget(button6) 30 | 31 | self.setLayout(layout) 32 | 33 | 34 | if __name__ == "__main__": 35 | 36 | app = QApplication(sys.argv) 37 | button_with_size_policy_widget = ButtonWithSizePolicy() 38 | button_with_size_policy_widget.resize(500, 200) 39 | button_with_size_policy_widget.show() 40 | sys.exit(app.exec_()) 41 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/combobox_and_label.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtWidgets import QWidget, QApplication, QLabel, QComboBox, QVBoxLayout 2 | from PySide2.QtCore import Qt 3 | import sys 4 | 5 | 6 | class ComboBoxAndLabel(QWidget): 7 | 8 | def __init__(self): 9 | super(ComboBoxAndLabel, self).__init__() 10 | 11 | self.combobox = QComboBox() 12 | self.combobox.addItems(["Orange", "Apple", "Grape"]) 13 | self.combobox.currentTextChanged.connect(self.comboboxSelected) 14 | 15 | self.label = QLabel("label: before selecting combobox") 16 | 17 | layout = QVBoxLayout() 18 | layout.addWidget(self.combobox) 19 | layout.addWidget(self.label) 20 | 21 | self.setLayout(layout) 22 | 23 | def comboboxSelected(self, value): 24 | self.label.setText(value) 25 | 26 | 27 | if __name__ == "__main__": 28 | 29 | app = QApplication(sys.argv) 30 | combobox_and_label = ComboBoxAndLabel() 31 | combobox_and_label.show() 32 | sys.exit(app.exec_()) 33 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/create_grid_window.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtWidgets import QWidget, QApplication, QLabel, QGridLayout 2 | from PySide2.QtCore import Qt 3 | import sys 4 | 5 | 6 | class GridWindow(QWidget): 7 | 8 | def __init__(self): 9 | super(GridWindow, self).__init__() 10 | 11 | layout = QGridLayout() 12 | 13 | label = QLabel("Label A") 14 | layout.addWidget(label, 0, 0) 15 | 16 | label = QLabel("Label B") 17 | layout.addWidget(label, 1, 0) 18 | 19 | label = QLabel("Label C") 20 | layout.addWidget(label, 2, 0) 21 | 22 | label = QLabel("Label D") 23 | layout.addWidget(label, 3, 0) 24 | 25 | label = QLabel("Label E") 26 | layout.addWidget(label, 0, 1) 27 | 28 | label = QLabel("Label F") 29 | layout.addWidget(label, 0, 2) 30 | 31 | label = QLabel("Label G") 32 | label.setAlignment(Qt.AlignCenter) 33 | layout.addWidget(label, 1, 1, 2, 2) 34 | 35 | self.setLayout(layout) 36 | 37 | 38 | if __name__ == "__main__": 39 | 40 | app = QApplication(sys.argv) 41 | gridWindow = GridWindow() 42 | gridWindow.show() 43 | sys.exit(app.exec_()) 44 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/tabbed_window.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtWidgets import QTabWidget, QApplication, QWidget 2 | import sys 3 | from button_and_label import ButtonAndLabel 4 | 5 | 6 | class TabbedWindow(QTabWidget): 7 | 8 | def __init__(self, parent=None): 9 | super(TabbedWindow, self).__init__(parent) 10 | widget1 = QWidget() 11 | self.widget2 = ButtonAndLabel() 12 | widget3 = QWidget() 13 | self.addTab(widget1, "Tab 1") 14 | self.addTab(self.widget2, "Tab 2") 15 | self.addTab(widget3, "Tab 3") 16 | 17 | 18 | if __name__ == "__main__": 19 | 20 | app = QApplication(sys.argv) 21 | tabbedWindow = TabbedWindow() 22 | tabbedWindow.show() 23 | sys.exit(app.exec_()) 24 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/test_button_and_dialog.py: -------------------------------------------------------------------------------- 1 | from button_and_dialog import ButtonAndDialog 2 | from PySide2.QtWidgets import QInputDialog 3 | from PySide2 import QtCore 4 | 5 | 6 | def test_button_and_dialog(qtbot, monkeypatch): 7 | widget = ButtonAndDialog() 8 | qtbot.addWidget(widget) 9 | 10 | monkeypatch.setattr(QInputDialog, 'getText', lambda *args: ("New Text", True)) 11 | qtbot.mouseClick(widget.button, QtCore.Qt.LeftButton) 12 | 13 | assert widget.label.text() == "New Text" 14 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/test_button_and_label.py: -------------------------------------------------------------------------------- 1 | from button_and_label import ButtonAndLabel 2 | from PySide2 import QtCore 3 | 4 | 5 | def test_button_and_label(qtbot): 6 | widget = ButtonAndLabel() 7 | qtbot.addWidget(widget) 8 | 9 | assert widget.label.text() == "label: before clicked" 10 | 11 | qtbot.mouseClick(widget.button, QtCore.Qt.LeftButton) 12 | 13 | assert widget.label.text() == "label: after clicked" 14 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/test_button_and_list.py: -------------------------------------------------------------------------------- 1 | from button_and_list import ButtonAndList 2 | from PySide2 import QtCore 3 | 4 | 5 | def test_button_and_list(qtbot): 6 | widget = ButtonAndList() 7 | qtbot.addWidget(widget) 8 | 9 | qtbot.mouseClick(widget.button, QtCore.Qt.LeftButton) 10 | qtbot.mouseClick(widget.button, QtCore.Qt.LeftButton) 11 | qtbot.mouseClick(widget.button, QtCore.Qt.LeftButton) 12 | 13 | label_item = widget.v_layout.takeAt(2) 14 | assert label_item.widget().text() == "3" 15 | 16 | label_item = widget.v_layout.takeAt(1) 17 | assert label_item.widget().text() == "2" 18 | 19 | label_item = widget.v_layout.takeAt(0) 20 | assert label_item.widget().text() == "1" 21 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/test_button_and_long_process.py: -------------------------------------------------------------------------------- 1 | from button_and_long_process import ButtonAndLongProcess 2 | from PySide2 import QtCore 3 | 4 | 5 | def test_button_and_long_process(qtbot): 6 | widget = ButtonAndLongProcess() 7 | qtbot.addWidget(widget) 8 | 9 | assert widget.label.text() == "label: before clicked" 10 | 11 | qtbot.mouseClick(widget.button, QtCore.Qt.LeftButton) 12 | 13 | def check_label(): 14 | assert widget.label.text() == "label: after clicked" 15 | 16 | qtbot.waitUntil(check_label, 4000) 17 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/test_combobox_and_label.py: -------------------------------------------------------------------------------- 1 | from combobox_and_label import ComboBoxAndLabel 2 | from PySide2 import QtCore 3 | 4 | 5 | def test_combobox_and_label(qtbot): 6 | widget = ComboBoxAndLabel() 7 | qtbot.addWidget(widget) 8 | 9 | assert widget.label.text() == "label: before selecting combobox" 10 | 11 | qtbot.keyClicks(widget.combobox, "Grape") 12 | 13 | assert widget.label.text() == "Grape" 14 | -------------------------------------------------------------------------------- /Chapter09/advanced_course_qt/test_tabbed_window.py: -------------------------------------------------------------------------------- 1 | from tabbed_window import TabbedWindow 2 | from PySide2 import QtCore 3 | 4 | 5 | def test_tabbed_window(qtbot): 6 | tabbed = TabbedWindow() 7 | qtbot.addWidget(tabbed) 8 | 9 | assert tabbed.widget2.label.text() == "label: before clicked" 10 | 11 | qtbot.mouseClick(tabbed.widget2.button, QtCore.Qt.LeftButton) 12 | 13 | assert tabbed.widget2.label.text() == "label: after clicked" 14 | -------------------------------------------------------------------------------- /Chapter09/chains/contracts/ERC20Token.vy: -------------------------------------------------------------------------------- 1 | Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256}) 2 | Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256}) 3 | 4 | name: public(bytes[10]) 5 | symbol: public(bytes[3]) 6 | totalSupply: public(uint256) 7 | decimals: public(uint256) 8 | balances: uint256[address] 9 | allowed: uint256[address][address] 10 | 11 | @public 12 | def __init__(): 13 | _initialSupply: uint256 = 1000 14 | _decimals: uint256 = 3 15 | self.totalSupply = _initialSupply * 10 ** _decimals 16 | self.balances[msg.sender] = self.totalSupply 17 | self.name = 'Haha Coin' 18 | self.symbol = 'HAH' 19 | self.decimals = _decimals 20 | log.Transfer(ZERO_ADDRESS, msg.sender, self.totalSupply) 21 | 22 | @public 23 | @constant 24 | def balanceOf(_owner: address) -> uint256: 25 | return self.balances[_owner] 26 | 27 | @public 28 | def transfer(_to: address, _amount: uint256) -> bool: 29 | assert self.balances[msg.sender] >= _amount 30 | self.balances[msg.sender] -= _amount 31 | self.balances[_to] += _amount 32 | log.Transfer(msg.sender, _to, _amount) 33 | 34 | return True 35 | 36 | @public 37 | def transferFrom(_from: address, _to: address, _value: uint256) -> bool: 38 | assert _value <= self.allowed[_from][msg.sender] 39 | assert _value <= self.balances[_from] 40 | 41 | self.balances[_from] -= _value 42 | self.allowed[_from][msg.sender] -= _value 43 | self.balances[_to] += _value 44 | log.Transfer(_from, _to, _value) 45 | 46 | return True 47 | 48 | @public 49 | def approve(_spender: address, _amount: uint256) -> bool: 50 | self.allowed[msg.sender][_spender] = _amount 51 | log.Approval(msg.sender, _spender, _amount) 52 | 53 | return True 54 | 55 | @public 56 | @constant 57 | def allowance(_owner: address, _spender: address) -> uint256: 58 | return self.allowed[_owner][_spender] 59 | -------------------------------------------------------------------------------- /Chapter09/chains/contracts/ERC20Token2.vy: -------------------------------------------------------------------------------- 1 | Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256}) 2 | Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256}) 3 | 4 | name: public(bytes[10]) 5 | symbol: public(bytes[3]) 6 | totalSupply: public(uint256) 7 | decimals: public(uint256) 8 | balances: uint256[address] 9 | allowed: uint256[address][address] 10 | 11 | @public 12 | def __init__(): 13 | _initialSupply: uint256 = 2000 14 | _decimals: uint256 = 3 15 | self.totalSupply = _initialSupply * 10 ** _decimals 16 | self.balances[msg.sender] = self.totalSupply 17 | self.name = 'Momo Coin' 18 | self.symbol = 'MOM' 19 | self.decimals = _decimals 20 | log.Transfer(ZERO_ADDRESS, msg.sender, self.totalSupply) 21 | 22 | @public 23 | @constant 24 | def balanceOf(_owner: address) -> uint256: 25 | return self.balances[_owner] 26 | 27 | @public 28 | def transfer(_to: address, _amount: uint256) -> bool: 29 | assert self.balances[msg.sender] >= _amount 30 | self.balances[msg.sender] -= _amount 31 | self.balances[_to] += _amount 32 | log.Transfer(msg.sender, _to, _amount) 33 | 34 | return True 35 | 36 | @public 37 | def transferFrom(_from: address, _to: address, _value: uint256) -> bool: 38 | assert _value <= self.allowed[_from][msg.sender] 39 | assert _value <= self.balances[_from] 40 | 41 | self.balances[_from] -= _value 42 | self.allowed[_from][msg.sender] -= _value 43 | self.balances[_to] += _value 44 | log.Transfer(_from, _to, _value) 45 | 46 | return True 47 | 48 | @public 49 | def approve(_spender: address, _amount: uint256) -> bool: 50 | self.allowed[msg.sender][_spender] = _amount 51 | log.Approval(msg.sender, _spender, _amount) 52 | 53 | return True 54 | 55 | @public 56 | @constant 57 | def allowance(_owner: address, _spender: address) -> uint256: 58 | return self.allowed[_owner][_spender] 59 | -------------------------------------------------------------------------------- /Chapter09/chains/localblock/genesis.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 4 | "coinbase": "0x427af7b53b8f56adf6f13932bb17c42ed2a53d04", 5 | "extraData": "0x686f727365", 6 | "config": { 7 | "daoForkBlock": 0, 8 | "daoForSupport": true, 9 | "homesteadBlock": 0 10 | }, 11 | "timestamp": "0x0", 12 | "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", 13 | "nonce": "0xdeadbeefdeadbeef", 14 | "alloc": { 15 | "0x427af7b53b8f56adf6f13932bb17c42ed2a53d04":{ 16 | "balance": "1000000000000000000000000000000" 17 | } 18 | }, 19 | "gasLimit": "0x47d5cc", 20 | "difficulty": "0x01" 21 | } 22 | -------------------------------------------------------------------------------- /Chapter09/chains/localblock/init_chain.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | geth --rpc --rpcaddr 127.0.0.1 --rpcport 8545 --rpcapi admin,debug,eth,miner,net,personal,shh,txpool,web3,ws --ws --wsaddr 127.0.0.1 --wsport 8546 --wsapi admin,debug,eth,miner,net,personal,shh,txpool,web3,ws --datadir /home/arjuna/Documents/WritingBook/hands-on-blockchain-for-python-developers/chapter_09/chains/blockchain/chain_data --maxpeers 0 --networkid 1234 --port 30303 --ipcpath /tmp/tmpn_ztv58q/geth.ipc --nodiscover --mine --minerthreads 1 init /home/arjuna/Documents/WritingBook/hands-on-blockchain-for-python-developers/chapter_09/chains/blockchain/genesis.json -------------------------------------------------------------------------------- /Chapter09/chains/localblock/password: -------------------------------------------------------------------------------- 1 | this-is-not-a-secure-password 2 | -------------------------------------------------------------------------------- /Chapter09/chains/localblock/run_chain.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | geth --rpc --rpcaddr 127.0.0.1 --rpcport 8545 --rpcapi admin,debug,eth,miner,net,personal,shh,txpool,web3,ws --ws --wsaddr 127.0.0.1 --wsport 8546 --wsapi admin,debug,eth,miner,net,personal,shh,txpool,web3,ws --datadir /home/arjuna/Documents/WritingBook/hands-on-blockchain-for-python-developers/chapter_09/chains/localblock/chain_data --maxpeers 0 --networkid 1234 --port 30303 --ipcpath /tmp/geth.ipc --unlock 0x427af7b53b8f56adf6f13932bb17c42ed2a53d04 --password /home/arjuna/Documents/WritingBook/hands-on-blockchain-for-python-developers/chapter_09/chains/localblock/password --nodiscover --mine --minerthreads 1 3 | -------------------------------------------------------------------------------- /Chapter09/chains/registrar.json: -------------------------------------------------------------------------------- 1 | { 2 | "deployments": { 3 | "blockchain://5112f234674aacf2daccbe21a36193e59c69e751b6eb89e57b755f72898942c9/block/127083555db85eb7e86f82ced0e1e898418cb986e6d4979fdb5bb07a6789c392": { 4 | "ERC20Token2": { 5 | "0x26Eb6cdC6968C34B96b9343FcdB7b2E2b1769579": { 6 | "timestamp": 1544368351655658 7 | } 8 | } 9 | }, 10 | "blockchain://5112f234674aacf2daccbe21a36193e59c69e751b6eb89e57b755f72898942c9/block/ba5cdfa837a99b7f34684726952078393ed9b590b7148aa73bdbe43cd1ec740b": { 11 | "ERC20Token": { 12 | "0xd3B78F72AcE42c89774e03011d738f84683A76B1": { 13 | "timestamp": 1544365532955736 14 | } 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Chapter09/wallet/address.txt: -------------------------------------------------------------------------------- 1 | 0xd3B78F72AcE42c89774e03011d738f84683A76B1 2 | 0x26Eb6cdC6968C34B96b9343FcdB7b2E2b1769579 3 | -------------------------------------------------------------------------------- /Chapter09/wallet/icons/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter09/wallet/icons/ajax-loader.gif -------------------------------------------------------------------------------- /Chapter09/wallet/icons/copy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter09/wallet/images/427af7b53b8f56adf6f13932bb17c42ed2a53d04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter09/wallet/images/427af7b53b8f56adf6f13932bb17c42ed2a53d04.png -------------------------------------------------------------------------------- /Chapter09/wallet/images/6ad2ffd2e08bd73f5c50db60fdc82a58b0590b99.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter09/wallet/images/6ad2ffd2e08bd73f5c50db60fdc82a58b0590b99.png -------------------------------------------------------------------------------- /Chapter09/wallet/tests/test_account.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | sys.path.append(os.path.realpath(os.path.dirname(__file__)+"/..")) 3 | 4 | from wallet import WalletWidget 5 | from PySide2.QtWidgets import QInputDialog 6 | from PySide2 import QtCore 7 | 8 | 9 | def test_account(qtbot, monkeypatch): 10 | wallet = WalletWidget() 11 | qtbot.addWidget(wallet) 12 | 13 | old_accounts_amount = wallet.account_widget.accounts_layout.count() 14 | 15 | monkeypatch.setattr(QInputDialog, 'getText', lambda *args: ("password", True)) 16 | qtbot.mouseClick(wallet.account_widget.create_account_button, QtCore.Qt.LeftButton) 17 | 18 | accounts_amount = wallet.account_widget.accounts_layout.count() 19 | assert accounts_amount == old_accounts_amount + 1 20 | 21 | wallet.killThreads() 22 | -------------------------------------------------------------------------------- /Chapter09/wallet/tests/test_send.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | sys.path.append(os.path.realpath(os.path.dirname(__file__)+"/..")) 3 | from time import sleep 4 | 5 | from wallet import WalletWidget 6 | from PySide2.QtWidgets import QInputDialog 7 | from PySide2 import QtCore 8 | 9 | 10 | def test_send(qtbot, monkeypatch): 11 | wallet = WalletWidget() 12 | qtbot.addWidget(wallet) 13 | 14 | monkeypatch.setattr(QInputDialog, 'getText', lambda *args: ("this-is-not-a-secure-password", True)) 15 | 16 | first_account = wallet.send_widget.sender_items[0] 17 | second_account = wallet.send_widget.sender_items[1] 18 | 19 | qtbot.keyClicks(wallet.send_widget.sender_combo_box, second_account) 20 | old_balance_of_second_account = int(float(wallet.send_widget.balance_label.text().split()[1])) 21 | sleep(1) 22 | 23 | qtbot.keyClicks(wallet.send_widget.destination_line_edit, second_account) 24 | sleep(1) 25 | 26 | qtbot.keyClicks(wallet.send_widget.sender_combo_box, first_account) 27 | sleep(1) 28 | 29 | qtbot.keyClicks(wallet.send_widget.amount_line_edit, "10") 30 | sleep(1) 31 | 32 | qtbot.mouseClick(wallet.send_widget.send_button, QtCore.Qt.LeftButton) 33 | 34 | sleep(20) 35 | 36 | qtbot.keyClicks(wallet.send_widget.sender_combo_box, second_account) 37 | balance_of_second_account = int(float(wallet.send_widget.balance_label.text().split()[1])) 38 | 39 | assert balance_of_second_account - old_balance_of_second_account == 10 40 | 41 | wallet.killThreads() 42 | -------------------------------------------------------------------------------- /Chapter09/wallet/tests/test_token.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | sys.path.append(os.path.realpath(os.path.dirname(__file__)+"/..")) 3 | 4 | from wallet import WalletWidget 5 | from PySide2.QtWidgets import QInputDialog 6 | from PySide2 import QtCore 7 | 8 | 9 | def test_token(qtbot, monkeypatch): 10 | wallet = WalletWidget() 11 | qtbot.addWidget(wallet) 12 | 13 | old_tokens_amount = wallet.token_widget.tokens_layout.count() 14 | 15 | address = None 16 | with open('address.txt') as f: 17 | address = f.readline().rstrip() 18 | 19 | monkeypatch.setattr(QInputDialog, 'getText', lambda *args: (address, True)) 20 | qtbot.mouseClick(wallet.token_widget.watch_token_button, QtCore.Qt.LeftButton) 21 | 22 | tokens_amount = wallet.token_widget.tokens_layout.count() 23 | assert tokens_amount == old_tokens_amount + 1 24 | 25 | wallet.killThreads() 26 | -------------------------------------------------------------------------------- /Chapter09/wallet/tools/util.py: -------------------------------------------------------------------------------- 1 | from os.path import isdir, exists 2 | from os import mkdir 3 | from tools.identicon import render_identicon 4 | 5 | 6 | def render_avatar(code): 7 | code = int(code, 16) 8 | img_filename = 'images/%08x.png' % code 9 | if exists(img_filename): 10 | return img_filename 11 | img = render_identicon(code, 24) 12 | if not isdir('images'): 13 | mkdir('images') 14 | img.save(img_filename, 'PNG') 15 | return img_filename 16 | -------------------------------------------------------------------------------- /Chapter09/wallet/wallet.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtWidgets import QTabWidget, QApplication 2 | import sys 3 | 4 | from wallet_widgets.account_widget import AccountWidget 5 | from wallet_widgets.send_widget import SendWidget 6 | from wallet_widgets.token_widget import TokenWidget 7 | 8 | 9 | class WalletWidget(QTabWidget): 10 | 11 | def __init__(self, parent=None): 12 | super(WalletWidget, self).__init__(parent) 13 | self.account_widget = AccountWidget() 14 | self.send_widget = SendWidget() 15 | self.token_widget = TokenWidget() 16 | self.addTab(self.account_widget, "Account") 17 | self.addTab(self.send_widget, "Send") 18 | self.addTab(self.token_widget, "Token") 19 | 20 | def killThreads(self): 21 | self.account_widget.kill() 22 | 23 | 24 | if __name__ == "__main__": 25 | 26 | app = QApplication(sys.argv) 27 | wallet_widget = WalletWidget() 28 | wallet_widget.show() 29 | return_app = app.exec_() 30 | wallet_widget.killThreads() 31 | sys.exit(return_app) 32 | -------------------------------------------------------------------------------- /Chapter09/wallet/wallet_threads/balance_thread.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtCore import QThread, Signal 2 | from time import sleep 3 | from blockchain import blockchain 4 | 5 | 6 | class BalanceThread(QThread): 7 | 8 | get_balance_transaction = Signal(map) 9 | 10 | def __init__(self, parent=None): 11 | super(BalanceThread, self).__init__(parent) 12 | self.quit = False 13 | 14 | def kill(self): 15 | self.quit = True 16 | 17 | def run(self): 18 | while True: 19 | sleep(2) 20 | if self.quit: 21 | break 22 | accounts = blockchain.get_accounts() 23 | self.get_balance_transaction.emit(accounts) 24 | -------------------------------------------------------------------------------- /Chapter09/wallet/wallet_threads/send_thread.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtCore import QThread, Signal 2 | from blockchain import blockchain 3 | 4 | 5 | class SendThread(QThread): 6 | 7 | send_transaction = Signal() 8 | 9 | def __init__(self, parent=None): 10 | super(SendThread, self).__init__(parent) 11 | 12 | def prepareTransaction(self, tx): 13 | self.tx = tx 14 | 15 | def run(self): 16 | blockchain.create_send_transaction(self.tx) 17 | self.send_transaction.emit() 18 | -------------------------------------------------------------------------------- /Chapter09/wallet/wallet_threads/send_token_thread.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtCore import QThread, Signal 2 | from blockchain import blockchain 3 | 4 | 5 | class SendTokenThread(QThread): 6 | 7 | send_token_transaction = Signal() 8 | 9 | def __init__(self, parent=None): 10 | super(SendTokenThread, self).__init__(parent) 11 | 12 | def prepareTransaction(self, tx, token_information): 13 | self.tx = tx 14 | self.token_information = token_information 15 | 16 | def run(self): 17 | blockchain.create_send_token_transaction(self.tx, self.token_information) 18 | self.send_token_transaction.emit() 19 | -------------------------------------------------------------------------------- /Chapter09/wallet/wallet_widgets/account_widget.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtWidgets import (QWidget, 2 | QGridLayout, 3 | QVBoxLayout, 4 | QHBoxLayout, 5 | QPushButton, 6 | QLabel, 7 | QInputDialog, 8 | QLineEdit, 9 | QToolTip, 10 | QApplication, 11 | QSizePolicy) 12 | from PySide2.QtCore import Slot, SIGNAL, QSize 13 | from PySide2.QtGui import QPixmap, QIcon, QCursor, QClipboard 14 | from time import sleep 15 | from blockchain import blockchain 16 | from tools.util import render_avatar 17 | from wallet_threads.balance_thread import BalanceThread 18 | 19 | 20 | class AccountWidget(QWidget): 21 | 22 | balance_widgets = {} 23 | 24 | def __init__(self, parent=None): 25 | super(AccountWidget, self).__init__(parent) 26 | 27 | self.create_account_button = QPushButton("Create Account") 28 | self.create_account_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) 29 | self.connect(self.create_account_button, SIGNAL('clicked()'), self.createNewAccount) 30 | 31 | self.accounts_layout = QVBoxLayout() 32 | 33 | accounts = blockchain.get_accounts() 34 | 35 | for account, balance in accounts: 36 | self._addAccountToWindow(account, balance) 37 | 38 | layout = QGridLayout() 39 | 40 | layout.addWidget(self.create_account_button, 0, 0) 41 | layout.addLayout(self.accounts_layout, 1, 0) 42 | 43 | self.setLayout(layout) 44 | 45 | self.balance_thread = BalanceThread() 46 | self.balance_thread.get_balance_transaction.connect(self._updateBalances) 47 | self.balance_thread.start() 48 | 49 | @Slot() 50 | def createNewAccount(self): 51 | password, ok = QInputDialog.getText(self, "Create A New Account", 52 | "Password:", QLineEdit.Normal) 53 | if ok and password != '': 54 | new_account = blockchain.create_new_account(password) 55 | self._addAccountToWindow(new_account, 0, resize_parent=True) 56 | 57 | def copyAddress(self, address): 58 | QToolTip.showText(QCursor.pos(), "Address %s has been copied to clipboard!" % address) 59 | clipboard = QApplication.clipboard() 60 | clipboard.setText(address) 61 | 62 | def _addAccountToWindow(self, account, balance, resize_parent=False): 63 | wrapper_layout = QVBoxLayout() 64 | account_layout = QHBoxLayout() 65 | rows_layout = QVBoxLayout() 66 | address_layout = QHBoxLayout() 67 | account_label = QLabel(account) 68 | account_label.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) 69 | copy_button = QPushButton() 70 | copy_button.setAutoFillBackground(True) 71 | copy_button.setIcon(QIcon('icons/copy.svg')) 72 | self.connect(copy_button, SIGNAL('clicked()'), lambda: self.copyAddress(account)) 73 | address_layout.addWidget(account_label) 74 | address_layout.addWidget(copy_button) 75 | balance_label = QLabel('Balance: %.5f ethers' % balance) 76 | balance_label.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) 77 | self.balance_widgets[account] = balance_label 78 | rows_layout.addLayout(address_layout) 79 | rows_layout.addWidget(balance_label) 80 | avatar = QLabel() 81 | img_filename = render_avatar(account) 82 | pixmap = QPixmap(img_filename) 83 | avatar.setPixmap(pixmap) 84 | account_layout.addWidget(avatar) 85 | account_layout.addLayout(rows_layout) 86 | wrapper_layout.addLayout(account_layout) 87 | wrapper_layout.addSpacing(20) 88 | self.accounts_layout.addLayout(wrapper_layout) 89 | 90 | if resize_parent: 91 | sizeHint = self.sizeHint() 92 | self.parentWidget().parentWidget().resize(QSize(sizeHint.width(), sizeHint.height() + 40)) 93 | 94 | def kill(self): 95 | self.balance_thread.kill() 96 | sleep(2) 97 | 98 | @Slot() 99 | def _updateBalances(self, accounts): 100 | for account, balance in accounts: 101 | self.balance_widgets[account].setText('Balance: %.5f ethers' % balance) 102 | -------------------------------------------------------------------------------- /Chapter09/wallet/wallet_widgets/token_widget.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtWidgets import (QWidget, 2 | QGridLayout, 3 | QVBoxLayout, 4 | QHBoxLayout, 5 | QPushButton, 6 | QLabel, 7 | QInputDialog, 8 | QLineEdit, 9 | QToolTip, 10 | QComboBox, 11 | QApplication, 12 | QSlider, 13 | QSizePolicy) 14 | from PySide2.QtCore import Slot, SIGNAL, QSize, Qt 15 | from PySide2.QtGui import QPixmap, QMovie, QPalette, QColor 16 | from os.path import isdir, exists 17 | from os import mkdir 18 | from time import sleep 19 | import json 20 | from tools.util import render_avatar 21 | from blockchain import blockchain, SendTransaction, TokenInformation 22 | 23 | 24 | class TokenWidget(QWidget): 25 | 26 | tokens_file = 'tokens.json' 27 | 28 | def __init__(self, parent=None): 29 | super(TokenWidget, self).__init__(parent) 30 | 31 | self.watch_token_button = QPushButton("Watch Token") 32 | 33 | tokens = blockchain.get_tokens() 34 | 35 | self.tokens_layout = QVBoxLayout() 36 | 37 | for address, token_from_json in tokens.items(): 38 | token_information = blockchain.get_token_named_tuple(token_from_json, address) 39 | self._addTokenToWindow(token_information) 40 | 41 | layout = QGridLayout() 42 | 43 | self.watch_token_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) 44 | self.connect(self.watch_token_button, SIGNAL('clicked()'), self.watchNewToken) 45 | 46 | layout.addWidget(self.watch_token_button, 0, 0) 47 | layout.addLayout(self.tokens_layout, 1, 0) 48 | 49 | self.setLayout(layout) 50 | 51 | def _addTokenToWindow(self, token_information, resize_parent=False): 52 | wrapper_layout = QVBoxLayout() 53 | token_layout = QHBoxLayout() 54 | rows_layout = QVBoxLayout() 55 | token_label = QLabel(token_information.name) 56 | token_label.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) 57 | symbol_label = QLabel(token_information.symbol) 58 | symbol_label.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) 59 | total_supply_label = QLabel('Total Supply: %d coins' % token_information.totalSupply) 60 | total_supply_label.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) 61 | rows_layout.addWidget(token_label) 62 | rows_layout.addWidget(symbol_label) 63 | rows_layout.addWidget(total_supply_label) 64 | avatar = QLabel() 65 | img_filename = render_avatar(token_information.address) 66 | pixmap = QPixmap(img_filename) 67 | avatar.setPixmap(pixmap) 68 | token_layout.addWidget(avatar) 69 | token_layout.addLayout(rows_layout) 70 | wrapper_layout.addLayout(token_layout) 71 | wrapper_layout.addSpacing(20) 72 | self.tokens_layout.addLayout(wrapper_layout) 73 | 74 | if resize_parent: 75 | sizeHint = self.size() 76 | self.parentWidget().parentWidget().resize(QSize(sizeHint.width(), sizeHint.height() + 100)) 77 | 78 | @Slot() 79 | def watchNewToken(self): 80 | address, ok = QInputDialog.getText(self, "Watch A New Token", 81 | "Token Smart Contract:", QLineEdit.Normal) 82 | if ok and address != '': 83 | token_information = blockchain.get_information_of_token(address) 84 | self._addTokenToWindow(token_information, resize_parent=True) 85 | token_data = {} 86 | if exists(self.tokens_file): 87 | with open(self.tokens_file) as json_data: 88 | token_data = json.load(json_data) 89 | token_data[token_information.address] = {'name': token_information.name, 90 | 'symbol': token_information.symbol, 91 | 'total_supply': token_information.totalSupply} 92 | with open(self.tokens_file, 'w') as outfile: 93 | json.dump(token_data, outfile) 94 | -------------------------------------------------------------------------------- /Chapter10/create_hash_from_content.py: -------------------------------------------------------------------------------- 1 | from os import listdir 2 | from hashlib import sha256 3 | 4 | 5 | files = [f for f in listdir('.') if 'hello' in f] 6 | 7 | hashes = {} 8 | 9 | for file in files: 10 | with open(file) as f: 11 | content = f.read().encode('utf-8') 12 | hash_of_content = sha256(content).hexdigest() 13 | hashes[hash_of_content] = content 14 | 15 | content = hashes['20c38a7a55fc8a8e7f45fde7247a0436d97826c20c5e7f8c978e6d59fa895fd2'] 16 | print(content.decode('utf-8')) 17 | 18 | print(len(hashes)) 19 | -------------------------------------------------------------------------------- /Chapter10/hash_big_file.py: -------------------------------------------------------------------------------- 1 | from os import listdir 2 | from hashlib import sha256 3 | from merkle_tree import MerkleTree 4 | 5 | 6 | hashes = {} 7 | 8 | file = 'hello_big.txt' 9 | with open(file) as f: 10 | lines = f.read().split('\n') 11 | hash = [] 12 | hash_of_hash = [] 13 | merkle_tree = MerkleTree(lines) 14 | root_hash = merkle_tree.root_hash 15 | 16 | hashes[root_hash] = [] 17 | for line in lines: 18 | hashes[root_hash].append(line) 19 | 20 | print(hashes) 21 | -------------------------------------------------------------------------------- /Chapter10/hash_directory.py: -------------------------------------------------------------------------------- 1 | from merkle_dag import MerkleDAGNode 2 | 3 | 4 | outer_directory = 'sample_directory' 5 | 6 | node = MerkleDAGNode(outer_directory) 7 | print(node) 8 | print(node.content) 9 | -------------------------------------------------------------------------------- /Chapter10/hello.txt: -------------------------------------------------------------------------------- 1 | I am a good boy. 2 | -------------------------------------------------------------------------------- /Chapter10/hello2.txt: -------------------------------------------------------------------------------- 1 | I am a good girl. 2 | -------------------------------------------------------------------------------- /Chapter10/hello3.txt: -------------------------------------------------------------------------------- 1 | I am a good horse. 2 | -------------------------------------------------------------------------------- /Chapter10/hello4.txt: -------------------------------------------------------------------------------- 1 | I am a good girl. 2 | -------------------------------------------------------------------------------- /Chapter10/hello_big.txt: -------------------------------------------------------------------------------- 1 | I am a big boy. 2 | I am a tall girl. 3 | I am a fast horse. 4 | I am a slow dragon. 5 | -------------------------------------------------------------------------------- /Chapter10/merkle_dag.py: -------------------------------------------------------------------------------- 1 | from os import listdir 2 | from hashlib import sha256 3 | from os.path import isdir 4 | from pathlib import Path 5 | from merkle_tree import MerkleTree 6 | 7 | 8 | class MerkleDAGNode: 9 | 10 | def __init__(self, filepath : str): 11 | self.pointers = {} 12 | self.dirtype = isdir(filepath) 13 | self.filename = Path(filepath).name 14 | if not self.dirtype: 15 | with open(filepath) as f: 16 | self.content = f.read() 17 | self.hash = self._hash((self.filename + self.content).encode('utf-8')) 18 | else: 19 | self.content = self._iterate_directory_contents(filepath) 20 | nodes_in_str_array = list(map(lambda x: str(x), self.content)) 21 | if nodes_in_str_array: 22 | self.hash = self._hash((self.filename + MerkleTree(nodes_in_str_array).root_hash).encode('utf-8')) 23 | else: 24 | self.hash = self._hash(self.filename.encode('utf-8')) 25 | 26 | def _hash(self, data : bytes) -> bytes: 27 | return sha256(data).hexdigest() 28 | 29 | def _iterate_directory_contents(self, directory : str): 30 | nodes = [] 31 | for f in listdir(directory): 32 | merkle_dag_node = MerkleDAGNode(directory + '/' + f) 33 | nodes.append(merkle_dag_node) 34 | self.pointers[f] = merkle_dag_node 35 | return nodes 36 | 37 | def __repr__(self): 38 | return 'MerkleDAGNode: ' + self.hash + ' || ' + self.filename 39 | 40 | def __eq__(self, other): 41 | if isinstance(other, MerkleDAGNode): 42 | return self.hash == other.hash 43 | return False 44 | -------------------------------------------------------------------------------- /Chapter10/merkle_tree.py: -------------------------------------------------------------------------------- 1 | from hashlib import sha256 2 | from typing import List 3 | from math import ceil 4 | 5 | 6 | class MerkleTree: 7 | 8 | def __init__(self, leaf_nodes : List[str]): 9 | self.hash_nodes : List[str] = [] 10 | self.leaf_nodes : List[str] = leaf_nodes 11 | self._turn_leaf_nodes_to_hash_nodes() 12 | if len(leaf_nodes) < 4: 13 | self.root_hash = self._hash_list() 14 | else: 15 | self.root_hash = self._build_root_hash() 16 | 17 | def _hash_list(self): 18 | long_node = "".join(self.hash_nodes) 19 | return self._hash(long_node.encode('utf-8')) 20 | 21 | def _turn_leaf_nodes_to_hash_nodes(self): 22 | for node in self.leaf_nodes: 23 | self.hash_nodes.append(self._hash(node.encode('utf-8'))) 24 | 25 | def _hash(self, data : bytes) -> bytes: 26 | return sha256(data).hexdigest() 27 | 28 | def _build_root_hash(self) -> bytes: 29 | parent_amount = ceil(len(self.hash_nodes) / 2) 30 | nodes : List[str] = self.hash_nodes 31 | 32 | while parent_amount > 1: 33 | parents : List[bytes] = [] 34 | i = 0 35 | while i < len(nodes): 36 | node1 = nodes[i] 37 | if i + 1 >= len(nodes): 38 | node2 = None 39 | else: 40 | node2 = nodes[i+1] 41 | parents.append(self._convert_parent_from_two_nodes(node1, node2)) 42 | i += 2 43 | parent_amount = len(parents) 44 | nodes = parents 45 | 46 | return parents[0] 47 | 48 | def _convert_parent_from_two_nodes(self, node1 : bytes, node2) -> bytes: 49 | if node2 == None: 50 | return self._hash((node1 + node1).encode('utf-8')) 51 | return self._hash((node1 + node2).encode('utf-8')) 52 | 53 | 54 | if __name__ == '__main__': 55 | leaf_nodes = ['cat', 'dog', 'bird', 'whale'] 56 | merkle_tree = MerkleTree(leaf_nodes) 57 | # ba2b19423245563949658c3f98ebbc337671706e037267a7d95be78dc95f0f31 58 | print(merkle_tree.root_hash) 59 | 60 | leaf_nodes = ['cat', 'dog', 'bird', 'whale', 'unicorn', 'pegasus', 'elephant', 'mouse'] 61 | merkle_tree = MerkleTree(leaf_nodes) 62 | # 20a9a6aa60cc29d62189a5a4d2388a272b2a0f249264dd0da3a7eecdfdc044bd 63 | print(merkle_tree.root_hash) 64 | 65 | leaf_nodes = ['cat', 'dog', 'bird', 'whale', 'unicorn'] 66 | merkle_tree = MerkleTree(leaf_nodes) 67 | # 6182e4e70b821ecc35d788bb4801f9b26bec75e935129e3fcd66648a5fdd79f4 68 | print(merkle_tree.root_hash) 69 | 70 | leaf_nodes = ['cat', 'dog', 'bird', 'whale', 'unicorn', 'pegasus', 'elephant'] 71 | merkle_tree = MerkleTree(leaf_nodes) 72 | # 8149c6504e532f0b03631ab3779f44b4f72edcef7e842120fe2a9198fac8a08e 73 | print(merkle_tree.root_hash) 74 | 75 | leaf_nodes = ['cat', 'dog'] 76 | merkle_tree = MerkleTree(leaf_nodes) 77 | # d2cc68f933eebbeb605e65d4fa80ab8a76b87a40fce6fab348431f2f0c199603 78 | print(merkle_tree.root_hash) 79 | -------------------------------------------------------------------------------- /Chapter10/sample_directory/hello.txt: -------------------------------------------------------------------------------- 1 | I am a good boy. 2 | -------------------------------------------------------------------------------- /Chapter10/sample_directory/hello2.txt: -------------------------------------------------------------------------------- 1 | I am a good girl. 2 | -------------------------------------------------------------------------------- /Chapter10/sample_directory/hello3.txt: -------------------------------------------------------------------------------- 1 | I am a good horse. 2 | -------------------------------------------------------------------------------- /Chapter10/sample_directory/inner_directory/hello4.txt: -------------------------------------------------------------------------------- 1 | I am a good girl. 2 | -------------------------------------------------------------------------------- /Chapter10/test_merkle_dag_node.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from merkle_dag import MerkleDAGNode 3 | 4 | 5 | outer_directory = 'sample_directory' 6 | file1 = 'hello.txt' 7 | file2 = 'hello2.txt' 8 | file3 = 'hello3.txt' 9 | inner_directory = 'inner_directory' 10 | inner_file = 'hello4.txt' 11 | 12 | 13 | class TestMerkleDAGNode(unittest.TestCase): 14 | 15 | def test_file_node(self): 16 | file_node = MerkleDAGNode(outer_directory + '/' + file1) 17 | self.assertEqual(file_node.filename, file1) 18 | self.assertEqual(file_node.hash, '8ced218a323755a7d4969187449177bb2338658c354c7174e21285b579ae2bca') 19 | self.assertEqual(file_node.content, 'I am a good boy.\n') 20 | 21 | def test_directory_with_single_file_node(self): 22 | dir_node = MerkleDAGNode(outer_directory + '/' + inner_directory) 23 | self.assertEqual(dir_node.filename, inner_directory) 24 | self.assertEqual(dir_node.hash, 'c075280aef64223bd38b1bed1017599852180a37baa0eacce28bb92ac5492eb9') 25 | content_of_directory = [ 26 | MerkleDAGNode(outer_directory + '/' + inner_directory + '/' + inner_file), 27 | ] 28 | self.assertEqual(dir_node.content[0], content_of_directory[0]) 29 | 30 | def test_directory_with_multiple_files_and_single_directory_node(self): 31 | dir_node = MerkleDAGNode(outer_directory) 32 | self.assertEqual(dir_node.filename, outer_directory) 33 | self.assertEqual(dir_node.hash, 'ec618189b9de0dae250ab5fa0fd9bf1abc158935c66ff8595446f5f9b929e037') 34 | content_of_directory = [ 35 | MerkleDAGNode(outer_directory + '/' + file1), 36 | MerkleDAGNode(outer_directory + '/' + file2), 37 | MerkleDAGNode(outer_directory + '/' + file3), 38 | MerkleDAGNode(outer_directory + '/' + inner_directory) 39 | ] 40 | self.assertEqual(dir_node.pointers[file1], content_of_directory[0]) 41 | self.assertEqual(dir_node.pointers[file2], content_of_directory[1]) 42 | self.assertEqual(dir_node.pointers[file3], content_of_directory[2]) 43 | self.assertEqual(dir_node.pointers[inner_directory], content_of_directory[3]) 44 | 45 | 46 | if __name__ == '__main__': 47 | unittest.main() 48 | -------------------------------------------------------------------------------- /Chapter11/add_directory.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | import pprint 3 | 4 | 5 | c = ipfsapi.connect() 6 | result = c.add('mysite', True) 7 | 8 | pp = pprint.PrettyPrinter(indent=2) 9 | pp.pprint(result) 10 | -------------------------------------------------------------------------------- /Chapter11/add_file.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | result = c.add('hello.txt') 6 | 7 | print(result) 8 | -------------------------------------------------------------------------------- /Chapter11/add_horoscope_predictions.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | result = c.add('horoscope1.txt') 6 | print(result) 7 | result = c.add('horoscope2.txt') 8 | print(result) 9 | -------------------------------------------------------------------------------- /Chapter11/add_image_file.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | 6 | result = c.add('milada-vigerova-1284157-unsplash.jpg') 7 | print(result) 8 | -------------------------------------------------------------------------------- /Chapter11/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter11/cat.jpg -------------------------------------------------------------------------------- /Chapter11/cat_cute_cat.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | result = c.cat('QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg') 6 | with open('cat.jpg', 'wb') as f: 7 | f.write(result) 8 | -------------------------------------------------------------------------------- /Chapter11/construct_image_from_blocks.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | 6 | images_bytes = [] 7 | 8 | blocks = c.ls('QmV5KPoHHqbq2NsALniERnaYjCJPi3UxLnpwdTkV1EbNZM') 9 | for block in blocks['Objects'][0]['Links']: 10 | bytes = c.cat(block['Hash']) 11 | images_bytes.append(bytes) 12 | 13 | images = b''.join(images_bytes) 14 | with open('image_from_blocks.jpg', 'wb') as f: 15 | f.write(images) 16 | -------------------------------------------------------------------------------- /Chapter11/copy_in_mfs.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | c.files_cp('/ipfs/QmY8zTocoVNDJWUr33nhksBiZ3hxugFPhb6qSzpE761bVN', '/46MB_cute_bear.mp4') 6 | 7 | print(c.files_ls('/')) 8 | -------------------------------------------------------------------------------- /Chapter11/crypto.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | package crypto; 4 | 5 | message CryptoCurrency { 6 | required string name = 1; 7 | optional int32 total_supply = 2; 8 | 9 | enum CryptoType { 10 | BITCOIN = 0; 11 | ERC20 = 1; 12 | PRIVATE = 2; 13 | } 14 | 15 | required CryptoType type = 3 [default = ERC20]; 16 | } 17 | -------------------------------------------------------------------------------- /Chapter11/crypto_pb2.py: -------------------------------------------------------------------------------- 1 | # Generated by the protocol buffer compiler. DO NOT EDIT! 2 | # source: crypto.proto 3 | 4 | import sys 5 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import message as _message 8 | from google.protobuf import reflection as _reflection 9 | from google.protobuf import symbol_database as _symbol_database 10 | from google.protobuf import descriptor_pb2 11 | # @@protoc_insertion_point(imports) 12 | 13 | _sym_db = _symbol_database.Default() 14 | 15 | 16 | 17 | 18 | DESCRIPTOR = _descriptor.FileDescriptor( 19 | name='crypto.proto', 20 | package='crypto', 21 | serialized_pb=_b('\n\x0c\x63rypto.proto\x12\x06\x63rypto\"\x9f\x01\n\x0e\x43ryptoCurrency\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x14\n\x0ctotal_supply\x18\x02 \x01(\x05\x12\x36\n\x04type\x18\x03 \x02(\x0e\x32!.crypto.CryptoCurrency.CryptoType:\x05\x45RC20\"1\n\nCryptoType\x12\x0b\n\x07\x42ITCOIN\x10\x00\x12\t\n\x05\x45RC20\x10\x01\x12\x0b\n\x07PRIVATE\x10\x02') 22 | ) 23 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 24 | 25 | 26 | 27 | _CRYPTOCURRENCY_CRYPTOTYPE = _descriptor.EnumDescriptor( 28 | name='CryptoType', 29 | full_name='crypto.CryptoCurrency.CryptoType', 30 | filename=None, 31 | file=DESCRIPTOR, 32 | values=[ 33 | _descriptor.EnumValueDescriptor( 34 | name='BITCOIN', index=0, number=0, 35 | options=None, 36 | type=None), 37 | _descriptor.EnumValueDescriptor( 38 | name='ERC20', index=1, number=1, 39 | options=None, 40 | type=None), 41 | _descriptor.EnumValueDescriptor( 42 | name='PRIVATE', index=2, number=2, 43 | options=None, 44 | type=None), 45 | ], 46 | containing_type=None, 47 | options=None, 48 | serialized_start=135, 49 | serialized_end=184, 50 | ) 51 | _sym_db.RegisterEnumDescriptor(_CRYPTOCURRENCY_CRYPTOTYPE) 52 | 53 | 54 | _CRYPTOCURRENCY = _descriptor.Descriptor( 55 | name='CryptoCurrency', 56 | full_name='crypto.CryptoCurrency', 57 | filename=None, 58 | file=DESCRIPTOR, 59 | containing_type=None, 60 | fields=[ 61 | _descriptor.FieldDescriptor( 62 | name='name', full_name='crypto.CryptoCurrency.name', index=0, 63 | number=1, type=9, cpp_type=9, label=2, 64 | has_default_value=False, default_value=_b("").decode('utf-8'), 65 | message_type=None, enum_type=None, containing_type=None, 66 | is_extension=False, extension_scope=None, 67 | options=None), 68 | _descriptor.FieldDescriptor( 69 | name='total_supply', full_name='crypto.CryptoCurrency.total_supply', index=1, 70 | number=2, type=5, cpp_type=1, label=1, 71 | has_default_value=False, default_value=0, 72 | message_type=None, enum_type=None, containing_type=None, 73 | is_extension=False, extension_scope=None, 74 | options=None), 75 | _descriptor.FieldDescriptor( 76 | name='type', full_name='crypto.CryptoCurrency.type', index=2, 77 | number=3, type=14, cpp_type=8, label=2, 78 | has_default_value=True, default_value=1, 79 | message_type=None, enum_type=None, containing_type=None, 80 | is_extension=False, extension_scope=None, 81 | options=None), 82 | ], 83 | extensions=[ 84 | ], 85 | nested_types=[], 86 | enum_types=[ 87 | _CRYPTOCURRENCY_CRYPTOTYPE, 88 | ], 89 | options=None, 90 | is_extendable=False, 91 | extension_ranges=[], 92 | oneofs=[ 93 | ], 94 | serialized_start=25, 95 | serialized_end=184, 96 | ) 97 | 98 | _CRYPTOCURRENCY.fields_by_name['type'].enum_type = _CRYPTOCURRENCY_CRYPTOTYPE 99 | _CRYPTOCURRENCY_CRYPTOTYPE.containing_type = _CRYPTOCURRENCY 100 | DESCRIPTOR.message_types_by_name['CryptoCurrency'] = _CRYPTOCURRENCY 101 | 102 | CryptoCurrency = _reflection.GeneratedProtocolMessageType('CryptoCurrency', (_message.Message,), dict( 103 | DESCRIPTOR = _CRYPTOCURRENCY, 104 | __module__ = 'crypto_pb2' 105 | # @@protoc_insertion_point(class_scope:crypto.CryptoCurrency) 106 | )) 107 | _sym_db.RegisterMessage(CryptoCurrency) 108 | 109 | 110 | # @@protoc_insertion_point(module_scope) 111 | -------------------------------------------------------------------------------- /Chapter11/download_a_directory_of_cute_cat_picture.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | directory = 'QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ' 6 | c.get(directory) 7 | -------------------------------------------------------------------------------- /Chapter11/download_cute_cat_picture.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | cute_cat_picture = 'QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg' 6 | c.get(cute_cat_picture) 7 | -------------------------------------------------------------------------------- /Chapter11/exploring_mfs.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | import io 3 | 4 | 5 | c = ipfsapi.connect() 6 | 7 | print("By default our MFS is empty.") 8 | print(c.files_ls('/')) # root is / just like Unix filesystem 9 | 10 | print("We can create a directory in our MFS.") 11 | c.files_mkdir('/classical_movies') 12 | 13 | print("We can create a file in our MFS.") 14 | c.files_write('/classical_movies/titanic', 15 | io.BytesIO(b"The ship crashed. The end."), 16 | create=True) 17 | 18 | print("We can copy a file in our MFS.") 19 | c.files_cp('/classical_movies/titanic', 20 | '/classical_movies/copy_of_titanic') 21 | 22 | print("We can read the file.") 23 | print(c.files_read('/classical_movies/titanic')) 24 | 25 | print("We can remove the file.") 26 | print(c.files_rm('/classical_movies/copy_of_titanic')) 27 | 28 | print("Now our MFS is not empty anymore.") 29 | print(c.files_ls('/')) 30 | print(c.files_ls('/classical_movies')) 31 | 32 | print("You can get the hash of a path in MFS.") 33 | print(c.files_stat('/classical_movies')) 34 | 35 | print("Then you can publish this hash into IPNS.") 36 | -------------------------------------------------------------------------------- /Chapter11/generate_another_key.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | print(c.key_list()) 6 | 7 | c.key_gen('another_key', 'rsa') 8 | 9 | print(c.key_list()) 10 | -------------------------------------------------------------------------------- /Chapter11/get_unicorn.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | result = c.cat('QmY7MiYeySnsed1Z3KxqDVYuM8pfiT5gGTqprNaNhUpZgR') 6 | print(result) 7 | -------------------------------------------------------------------------------- /Chapter11/get_unicorn_block.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | result = c.block_get('QmY7MiYeySnsed1Z3KxqDVYuM8pfiT5gGTqprNaNhUpZgR') 6 | print(result) 7 | -------------------------------------------------------------------------------- /Chapter11/hello.txt: -------------------------------------------------------------------------------- 1 | I am a good unicorn. 2 | -------------------------------------------------------------------------------- /Chapter11/horoscope1.txt: -------------------------------------------------------------------------------- 1 | You will meet the love of your life today! 2 | -------------------------------------------------------------------------------- /Chapter11/horoscope2.txt: -------------------------------------------------------------------------------- 1 | You need to be careful when going outside! 2 | -------------------------------------------------------------------------------- /Chapter11/image_from_blocks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter11/image_from_blocks.jpg -------------------------------------------------------------------------------- /Chapter11/keys_list.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | print(c.key_list()) 6 | -------------------------------------------------------------------------------- /Chapter11/list_blocks.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | import pprint 3 | 4 | 5 | c = ipfsapi.connect() 6 | 7 | blocks = c.ls('QmV5KPoHHqbq2NsALniERnaYjCJPi3UxLnpwdTkV1EbNZM') 8 | 9 | pp = pprint.PrettyPrinter(indent=2) 10 | pp.pprint(blocks) 11 | -------------------------------------------------------------------------------- /Chapter11/merkledag.proto: -------------------------------------------------------------------------------- 1 | package merkledag.pb; 2 | 3 | // An IPFS MerkleDAG Link 4 | message PBLink { 5 | 6 | // multihash of the target object 7 | optional bytes Hash = 1; 8 | 9 | // utf string name. should be unique per object 10 | optional string Name = 2; 11 | 12 | // cumulative size of target object 13 | optional uint64 Tsize = 3; 14 | } 15 | 16 | // An IPFS MerkleDAG Node 17 | message PBNode { 18 | 19 | // refs to other objects 20 | repeated PBLink Links = 2; 21 | 22 | // opaque user data 23 | optional bytes Data = 1; 24 | } 25 | -------------------------------------------------------------------------------- /Chapter11/merkledag_pb2.py: -------------------------------------------------------------------------------- 1 | # Generated by the protocol buffer compiler. DO NOT EDIT! 2 | # source: merkledag.proto 3 | 4 | import sys 5 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import message as _message 8 | from google.protobuf import reflection as _reflection 9 | from google.protobuf import symbol_database as _symbol_database 10 | from google.protobuf import descriptor_pb2 11 | # @@protoc_insertion_point(imports) 12 | 13 | _sym_db = _symbol_database.Default() 14 | 15 | 16 | 17 | 18 | DESCRIPTOR = _descriptor.FileDescriptor( 19 | name='merkledag.proto', 20 | package='merkledag.pb', 21 | serialized_pb=_b('\n\x0fmerkledag.proto\x12\x0cmerkledag.pb\"3\n\x06PBLink\x12\x0c\n\x04Hash\x18\x01 \x01(\x0c\x12\x0c\n\x04Name\x18\x02 \x01(\t\x12\r\n\x05Tsize\x18\x03 \x01(\x04\";\n\x06PBNode\x12#\n\x05Links\x18\x02 \x03(\x0b\x32\x14.merkledag.pb.PBLink\x12\x0c\n\x04\x44\x61ta\x18\x01 \x01(\x0c') 22 | ) 23 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 24 | 25 | 26 | 27 | 28 | _PBLINK = _descriptor.Descriptor( 29 | name='PBLink', 30 | full_name='merkledag.pb.PBLink', 31 | filename=None, 32 | file=DESCRIPTOR, 33 | containing_type=None, 34 | fields=[ 35 | _descriptor.FieldDescriptor( 36 | name='Hash', full_name='merkledag.pb.PBLink.Hash', index=0, 37 | number=1, type=12, cpp_type=9, label=1, 38 | has_default_value=False, default_value=_b(""), 39 | message_type=None, enum_type=None, containing_type=None, 40 | is_extension=False, extension_scope=None, 41 | options=None), 42 | _descriptor.FieldDescriptor( 43 | name='Name', full_name='merkledag.pb.PBLink.Name', index=1, 44 | number=2, type=9, cpp_type=9, label=1, 45 | has_default_value=False, default_value=_b("").decode('utf-8'), 46 | message_type=None, enum_type=None, containing_type=None, 47 | is_extension=False, extension_scope=None, 48 | options=None), 49 | _descriptor.FieldDescriptor( 50 | name='Tsize', full_name='merkledag.pb.PBLink.Tsize', index=2, 51 | number=3, type=4, cpp_type=4, label=1, 52 | has_default_value=False, default_value=0, 53 | message_type=None, enum_type=None, containing_type=None, 54 | is_extension=False, extension_scope=None, 55 | options=None), 56 | ], 57 | extensions=[ 58 | ], 59 | nested_types=[], 60 | enum_types=[ 61 | ], 62 | options=None, 63 | is_extendable=False, 64 | extension_ranges=[], 65 | oneofs=[ 66 | ], 67 | serialized_start=33, 68 | serialized_end=84, 69 | ) 70 | 71 | 72 | _PBNODE = _descriptor.Descriptor( 73 | name='PBNode', 74 | full_name='merkledag.pb.PBNode', 75 | filename=None, 76 | file=DESCRIPTOR, 77 | containing_type=None, 78 | fields=[ 79 | _descriptor.FieldDescriptor( 80 | name='Links', full_name='merkledag.pb.PBNode.Links', index=0, 81 | number=2, type=11, cpp_type=10, label=3, 82 | has_default_value=False, default_value=[], 83 | message_type=None, enum_type=None, containing_type=None, 84 | is_extension=False, extension_scope=None, 85 | options=None), 86 | _descriptor.FieldDescriptor( 87 | name='Data', full_name='merkledag.pb.PBNode.Data', index=1, 88 | number=1, type=12, cpp_type=9, label=1, 89 | has_default_value=False, default_value=_b(""), 90 | message_type=None, enum_type=None, containing_type=None, 91 | is_extension=False, extension_scope=None, 92 | options=None), 93 | ], 94 | extensions=[ 95 | ], 96 | nested_types=[], 97 | enum_types=[ 98 | ], 99 | options=None, 100 | is_extendable=False, 101 | extension_ranges=[], 102 | oneofs=[ 103 | ], 104 | serialized_start=86, 105 | serialized_end=145, 106 | ) 107 | 108 | _PBNODE.fields_by_name['Links'].message_type = _PBLINK 109 | DESCRIPTOR.message_types_by_name['PBLink'] = _PBLINK 110 | DESCRIPTOR.message_types_by_name['PBNode'] = _PBNODE 111 | 112 | PBLink = _reflection.GeneratedProtocolMessageType('PBLink', (_message.Message,), dict( 113 | DESCRIPTOR = _PBLINK, 114 | __module__ = 'merkledag_pb2' 115 | # @@protoc_insertion_point(class_scope:merkledag.pb.PBLink) 116 | )) 117 | _sym_db.RegisterMessage(PBLink) 118 | 119 | PBNode = _reflection.GeneratedProtocolMessageType('PBNode', (_message.Message,), dict( 120 | DESCRIPTOR = _PBNODE, 121 | __module__ = 'merkledag_pb2' 122 | # @@protoc_insertion_point(class_scope:merkledag.pb.PBNode) 123 | )) 124 | _sym_db.RegisterMessage(PBNode) 125 | 126 | 127 | # @@protoc_insertion_point(module_scope) 128 | -------------------------------------------------------------------------------- /Chapter11/milada-vigerova-1284157-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter11/milada-vigerova-1284157-unsplash.jpg -------------------------------------------------------------------------------- /Chapter11/mysite/README.md: -------------------------------------------------------------------------------- 1 | This is Readme file. 2 | -------------------------------------------------------------------------------- /Chapter11/mysite/img/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter11/mysite/img/cat.jpg -------------------------------------------------------------------------------- /Chapter11/mysite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Chapter11/nude_picture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Blockchain-for-Python-Developers/54a490f1b53b8c50cae48a93c509e04e05d05e9e/Chapter11/nude_picture.jpg -------------------------------------------------------------------------------- /Chapter11/publish_horoscope1.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | peer_id = c.key_list()['Keys'][0]['Id'] 6 | 7 | c.name_publish('QmYjYGKXqo36GDt6f6qvp9qKAsrc72R9y88mQSLvogu8Ub', key='another_list') 8 | 9 | result = c.cat('/ipns/' + peer_id) 10 | print(result) 11 | -------------------------------------------------------------------------------- /Chapter11/publish_horoscope1_in_another_ipns.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | peer_id = c.key_list()['Keys'][1]['Id'] 6 | 7 | c.name_publish('QmYjYGKXqo36GDt6f6qvp9qKAsrc72R9y88mQSLvogu8Ub', key='another_key') 8 | 9 | result = c.cat('/ipns/' + peer_id) 10 | print(result) 11 | -------------------------------------------------------------------------------- /Chapter11/publish_horoscope2.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | peer_id = c.key_list()['Keys'][0]['Id'] 6 | 7 | c.name_publish('Qme1FUeEhA1myqQ8C1sCSXo4dDJzZApGD6StE26S72ZqyU') 8 | 9 | result = c.cat('/ipns/' + peer_id) 10 | print(result) 11 | -------------------------------------------------------------------------------- /Chapter11/publish_topic.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | c.pubsub_pub('bitcoin', 'To the moon!') 6 | -------------------------------------------------------------------------------- /Chapter11/removing_nude_picture.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | 3 | 4 | c = ipfsapi.connect() 5 | c.pin_rm('QmWgMcTdPY9Rv7SCBusK1gWBRJcBi2MxNkC1yC6uvLYPwK') 6 | 7 | c.repo_gc() 8 | -------------------------------------------------------------------------------- /Chapter11/serialize_crypto_data.py: -------------------------------------------------------------------------------- 1 | import crypto_pb2 2 | 3 | cryptocurrency = crypto_pb2.CryptoCurrency() 4 | cryptocurrency.name = 'Bitcoin Cash' 5 | cryptocurrency.total_supply = 21000000 6 | cryptocurrency.type = crypto_pb2.CryptoCurrency.BITCOIN 7 | 8 | serialized_data = cryptocurrency.SerializeToString() 9 | print(serialized_data) 10 | 11 | cryptocurrency2 = crypto_pb2.CryptoCurrency() 12 | cryptocurrency2.ParseFromString(serialized_data) 13 | print(cryptocurrency2) 14 | -------------------------------------------------------------------------------- /Chapter11/serialize_unicorn.py: -------------------------------------------------------------------------------- 1 | import unixfs_pb2 2 | import merkledag_pb2 3 | 4 | precious_data = b'I am a good unicorn.\n' 5 | 6 | unicorn = unixfs_pb2.Data() 7 | unicorn.Type = unixfs_pb2.Data.File 8 | unicorn.Data = precious_data 9 | unicorn.filesize = len(precious_data) 10 | 11 | serialized_unicorn_node = unicorn.SerializeToString() 12 | 13 | outer_node = merkledag_pb2.PBNode() 14 | outer_node.Data = serialized_unicorn_node 15 | print(outer_node.SerializeToString()) 16 | -------------------------------------------------------------------------------- /Chapter11/subscribe_topic.py: -------------------------------------------------------------------------------- 1 | import ipfsapi 2 | from base64 import b64decode 3 | 4 | 5 | c = ipfsapi.connect() 6 | with c.pubsub_sub('bitcoin') as sub: 7 | for message in sub: 8 | string = b64decode(message['data']) 9 | print(string) 10 | break 11 | -------------------------------------------------------------------------------- /Chapter11/unixfs.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | package unixfs.pb; 4 | 5 | message Data { 6 | enum DataType { 7 | Raw = 0; 8 | Directory = 1; 9 | File = 2; 10 | Metadata = 3; 11 | Symlink = 4; 12 | HAMTShard = 5; 13 | } 14 | 15 | required DataType Type = 1; 16 | optional bytes Data = 2; 17 | optional uint64 filesize = 3; 18 | repeated uint64 blocksizes = 4; 19 | 20 | optional uint64 hashType = 5; 21 | optional uint64 fanout = 6; 22 | } 23 | 24 | message Metadata { 25 | optional string MimeType = 1; 26 | } 27 | -------------------------------------------------------------------------------- /Chapter11/unserialize_unicorn.py: -------------------------------------------------------------------------------- 1 | import unixfs_pb2 2 | import merkledag_pb2 3 | 4 | outer_node = merkledag_pb2.PBNode() 5 | outer_node.ParseFromString(b'\n\x1b\x08\x02\x12\x15I am a good unicorn.\n\x18\x15') 6 | print(outer_node) 7 | 8 | unicorn = unixfs_pb2.Data() 9 | unicorn.ParseFromString(outer_node.Data) 10 | print(unicorn) 11 | -------------------------------------------------------------------------------- /Chapter12/bootstrap_videos.py: -------------------------------------------------------------------------------- 1 | import os, json 2 | import ipfsapi 3 | from web3 import Web3, IPCProvider 4 | from populus.utils.wait import wait_for_transaction_receipt 5 | 6 | 7 | w3 = Web3(IPCProvider('/tmp/geth.ipc')) 8 | 9 | common_password = 'bitcoin123' 10 | accounts = [] 11 | with open('accounts.txt', 'w') as f: 12 | for i in range(4): 13 | account = w3.personal.newAccount(common_password) 14 | accounts.append(account) 15 | f.write(account + "\n") 16 | 17 | with open('address.txt', 'r') as f: 18 | address = f.read().rstrip("\n") 19 | 20 | with open('videos_sharing_smart_contract/build/contracts.json') as f: 21 | contract = json.load(f) 22 | abi = contract['VideosSharing']['abi'] 23 | 24 | VideosSharing = w3.eth.contract(address=address, abi=abi) 25 | 26 | c = ipfsapi.connect() 27 | 28 | coinbase = w3.eth.accounts[0] 29 | coinbase_password = 'this-is-not-a-secure-password' 30 | # Transfering Ethers 31 | for destination in accounts: 32 | nonce = w3.eth.getTransactionCount(Web3.toChecksumAddress(coinbase)) 33 | txn = { 34 | 'from': coinbase, 35 | 'to': Web3.toChecksumAddress(destination), 36 | 'value': w3.toWei('100', 'ether'), 37 | 'gas': 70000, 38 | 'gasPrice': w3.toWei('1', 'gwei'), 39 | 'nonce': nonce 40 | } 41 | txn_hash = w3.personal.sendTransaction(txn, coinbase_password) 42 | wait_for_transaction_receipt(w3, txn_hash) 43 | 44 | # Transfering Coins 45 | for destination in accounts: 46 | nonce = w3.eth.getTransactionCount(coinbase) 47 | txn = VideosSharing.functions.transfer(destination, 100).buildTransaction({ 48 | 'from': coinbase, 49 | 'gas': 70000, 50 | 'gasPrice': w3.toWei('1', 'gwei'), 51 | 'nonce': nonce 52 | }) 53 | txn_hash = w3.personal.sendTransaction(txn, coinbase_password) 54 | wait_for_transaction_receipt(w3, txn_hash) 55 | 56 | # Uploading Videos 57 | directory = 'stock_videos' 58 | movies = os.listdir(directory) 59 | length_of_movies = len(movies) 60 | for index, movie in enumerate(movies): 61 | account = accounts[index//7] 62 | ipfs_add = c.add(directory + '/' + movie) 63 | ipfs_path = ipfs_add['Hash'].encode('utf-8') 64 | title = movie.rstrip('.mp4')[:20].encode('utf-8') 65 | 66 | nonce = w3.eth.getTransactionCount(Web3.toChecksumAddress(account)) 67 | txn = VideosSharing.functions.upload_video(ipfs_path, title).buildTransaction({ 68 | 'from': account, 69 | 'gas': 200000, 70 | 'gasPrice': w3.toWei('30', 'gwei'), 71 | 'nonce': nonce 72 | }) 73 | txn_hash = w3.personal.sendTransaction(txn, common_password) 74 | wait_for_transaction_receipt(w3, txn_hash) 75 | -------------------------------------------------------------------------------- /Chapter12/check_bootstrap.py: -------------------------------------------------------------------------------- 1 | import json 2 | from web3 import Web3, IPCProvider 3 | 4 | 5 | w3 = Web3(IPCProvider('/tmp/geth.ipc')) 6 | 7 | with open('accounts.txt', 'r') as f: 8 | account = f.readline().rstrip("\n") 9 | 10 | with open('address.txt', 'r') as f: 11 | address = f.read().rstrip("\n") 12 | 13 | with open('videos_sharing_smart_contract/build/contracts.json') as f: 14 | contract = json.load(f) 15 | abi = contract['VideosSharing']['abi'] 16 | 17 | VideosSharing = w3.eth.contract(address=address, abi=abi) 18 | 19 | print(VideosSharing.functions.latest_videos_index(account).call()) 20 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/decentralized_videos/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for decentralized_videos project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.1.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.1/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'ppn*p5hw(gvm+%6frn((jno)83wofro_36#i2fj$ru_sgaz60)' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'videos' 41 | ] 42 | 43 | MIDDLEWARE = [ 44 | 'django.middleware.security.SecurityMiddleware', 45 | 'django.contrib.sessions.middleware.SessionMiddleware', 46 | 'django.middleware.common.CommonMiddleware', 47 | 'django.middleware.csrf.CsrfViewMiddleware', 48 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 49 | 'django.contrib.messages.middleware.MessageMiddleware', 50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 51 | ] 52 | 53 | ROOT_URLCONF = 'decentralized_videos.urls' 54 | 55 | TEMPLATES = [ 56 | { 57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 58 | 'DIRS': [], 59 | 'APP_DIRS': True, 60 | 'OPTIONS': { 61 | 'context_processors': [ 62 | 'django.template.context_processors.debug', 63 | 'django.template.context_processors.request', 64 | 'django.contrib.auth.context_processors.auth', 65 | 'django.contrib.messages.context_processors.messages', 66 | ], 67 | }, 68 | }, 69 | ] 70 | 71 | WSGI_APPLICATION = 'decentralized_videos.wsgi.application' 72 | 73 | 74 | # Database 75 | # https://docs.djangoproject.com/en/2.1/ref/settings/#databases 76 | 77 | DATABASES = { 78 | 'default': { 79 | 'ENGINE': 'django.db.backends.sqlite3', 80 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 81 | } 82 | } 83 | 84 | 85 | # Password validation 86 | # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators 87 | 88 | AUTH_PASSWORD_VALIDATORS = [ 89 | { 90 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 91 | }, 92 | { 93 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 94 | }, 95 | { 96 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 97 | }, 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 100 | }, 101 | ] 102 | 103 | 104 | # Internationalization 105 | # https://docs.djangoproject.com/en/2.1/topics/i18n/ 106 | 107 | LANGUAGE_CODE = 'en-us' 108 | 109 | TIME_ZONE = 'UTC' 110 | 111 | USE_I18N = True 112 | 113 | USE_L10N = True 114 | 115 | USE_TZ = True 116 | 117 | 118 | # Static files (CSS, JavaScript, Images) 119 | # https://docs.djangoproject.com/en/2.1/howto/static-files/ 120 | 121 | STATIC_URL = '/static/' 122 | 123 | STATICFILES_DIRS = [ 124 | os.path.join(BASE_DIR, "static"), 125 | ] 126 | 127 | MEDIA_URL = '/media/' 128 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 129 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/decentralized_videos/urls.py: -------------------------------------------------------------------------------- 1 | """decentralized_videos URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import include, path 18 | 19 | urlpatterns = [ 20 | path('', include('videos.urls')), 21 | path('admin/', admin.site.urls) 22 | ] 23 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/decentralized_videos/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for decentralized_videos project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'decentralized_videos.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == '__main__': 6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'decentralized_videos.settings') 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/videos/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/videos/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class VideosConfig(AppConfig): 5 | name = 'videos' 6 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/videos/models.py: -------------------------------------------------------------------------------- 1 | import os.path, json 2 | import ipfsapi 3 | import cv2 4 | from web3 import Web3, IPCProvider 5 | from populus.utils.wait import wait_for_transaction_receipt 6 | from decentralized_videos.settings import STATICFILES_DIRS, STATIC_URL, BASE_DIR, MEDIA_ROOT 7 | 8 | 9 | class VideosSharing: 10 | 11 | def __init__(self): 12 | self.w3 = Web3(IPCProvider('/tmp/geth.ipc')) 13 | with open('../address.txt', 'r') as f: 14 | address = f.read().rstrip("\n") 15 | 16 | with open('../videos_sharing_smart_contract/build/contracts.json') as f: 17 | contract = json.load(f) 18 | abi = contract['VideosSharing']['abi'] 19 | 20 | self.SmartContract = self.w3.eth.contract(address=address, abi=abi) 21 | 22 | self.ipfs_con = ipfsapi.connect() 23 | 24 | def recent_videos(self, amount=20): 25 | events = self.SmartContract.events.UploadVideo.createFilter(fromBlock=0).get_all_entries() 26 | videos = [] 27 | for event in events: 28 | video = {} 29 | video['user'] = event['args']['_user'] 30 | video['index'] = event['args']['_index'] 31 | video['path'] = self.get_video_path(video['user'], video['index']) 32 | video['title'] = self.get_video_title(video['user'], video['index']) 33 | video['thumbnail'] = self.get_video_thumbnail(video['path']) 34 | videos.append(video) 35 | videos.reverse() 36 | return videos[:amount] 37 | 38 | def get_videos(self, user, amount=20): 39 | latest_index = self.SmartContract.functions.latest_videos_index(user).call() 40 | i = 0 41 | videos = [] 42 | while i < amount and i < latest_index: 43 | video = {} 44 | index = latest_index - i - 1 45 | video['user'] = user 46 | video['index'] = index 47 | video['path'] = self.get_video_path(user, index) 48 | video['title'] = self.get_video_title(user, index) 49 | video['thumbnail'] = self.get_video_thumbnail(video['path']) 50 | videos.append(video) 51 | i += 1 52 | return videos 53 | 54 | def get_video_path(self, user, index): 55 | return self.SmartContract.functions.videos_path(user, index).call().decode('utf-8') 56 | 57 | def get_video_title(self, user, index): 58 | return self.SmartContract.functions.videos_title(user, index).call().decode('utf-8') 59 | 60 | def get_video_thumbnail(self, ipfs_path): 61 | thumbnail_file = STATICFILES_DIRS[0] + '/' + ipfs_path + '.png' 62 | url_file = STATIC_URL + '/' + ipfs_path + '.png' 63 | if os.path.isfile(thumbnail_file): 64 | return url_file 65 | else: 66 | return "https://bulma.io/images/placeholders/640x480.png" 67 | 68 | def get_video(self, user, index): 69 | video = {} 70 | ipfs_path = self.get_video_path(user, index) 71 | video_title = self.get_video_title(user, index) 72 | video_file = STATICFILES_DIRS[0] + '/' + ipfs_path + '.mp4' 73 | thumbnail_file = STATICFILES_DIRS[0] + '/' + ipfs_path + '.png' 74 | video['title'] = video_title 75 | video['user'] = user 76 | video['index'] = index 77 | video['aggregate_likes'] = self.SmartContract.functions.video_aggregate_likes(user, index).call() 78 | 79 | if os.path.isfile(video_file): 80 | video['url'] = STATIC_URL + '/' + ipfs_path + '.mp4' 81 | else: 82 | self.ipfs_con.get(ipfs_path) 83 | os.rename(BASE_DIR + '/' + ipfs_path, STATICFILES_DIRS[0] + '/' + ipfs_path + '.mp4') 84 | video['url'] = STATIC_URL + '/' + ipfs_path + '.mp4' 85 | 86 | if not os.path.isfile(thumbnail_file): 87 | self.process_thumbnail(ipfs_path) 88 | 89 | return video 90 | 91 | def upload_video(self, video_user, password, video_file, title): 92 | video_path = MEDIA_ROOT + '/video.mp4' 93 | with open(video_path, 'wb+') as destination: 94 | for chunk in video_file.chunks(): 95 | destination.write(chunk) 96 | ipfs_add = self.ipfs_con.add(video_path) 97 | ipfs_path = ipfs_add['Hash'].encode('utf-8') 98 | title = title[:20].encode('utf-8') 99 | nonce = self.w3.eth.getTransactionCount(Web3.toChecksumAddress(video_user)) 100 | txn = self.SmartContract.functions.upload_video(ipfs_path, title).buildTransaction({ 101 | 'from': video_user, 102 | 'gas': 200000, 103 | 'gasPrice': self.w3.toWei('30', 'gwei'), 104 | 'nonce': nonce 105 | }) 106 | txn_hash = self.w3.personal.sendTransaction(txn, password) 107 | wait_for_transaction_receipt(self.w3, txn_hash) 108 | 109 | def process_thumbnail(self, ipfs_path): 110 | thumbnail_file = STATICFILES_DIRS[0] + '/' + ipfs_path + '.png' 111 | if not os.path.isfile(thumbnail_file): 112 | video_path = STATICFILES_DIRS[0] + '/' + ipfs_path + '.mp4' 113 | cap = cv2.VideoCapture(video_path) 114 | cap.set(cv2.CAP_PROP_POS_FRAMES, 0) 115 | _, frame = cap.read() 116 | cv2.imwrite(thumbnail_file, frame) 117 | 118 | def like_video(self, video_liker, password, video_user, index): 119 | if self.SmartContract.functions.video_has_been_liked(video_liker, video_user, index).call(): 120 | return 121 | nonce = self.w3.eth.getTransactionCount(Web3.toChecksumAddress(video_liker)) 122 | txn = self.SmartContract.functions.like_video(video_user, index).buildTransaction({ 123 | 'from': video_liker, 124 | 'gas': 200000, 125 | 'gasPrice': self.w3.toWei('30', 'gwei'), 126 | 'nonce': nonce 127 | }) 128 | txn_hash = self.w3.personal.sendTransaction(txn, password) 129 | wait_for_transaction_receipt(self.w3, txn_hash) 130 | 131 | 132 | videos_sharing = VideosSharing() 133 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/videos/templates/videos/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Decentralized Videos Sharing Application 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 |

15 | Packt Pub Decentralized Videos Sharing Application 16 |

17 | 29 |
30 |
31 |
32 | {% block content %} 33 | {% endblock %} 34 | 35 | 36 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/videos/templates/videos/channel.html: -------------------------------------------------------------------------------- 1 | {% extends "videos/base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 | 12 | {% for video in videos %} 13 | {% cycle '
' '' '' '' %} 14 |
15 |
16 |
17 |
18 | 19 |
20 |
21 | 24 |
25 |
26 | {% cycle '' '' '' '
' %} 27 | {% endfor %} 28 |
29 |
30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/videos/templates/videos/index.html: -------------------------------------------------------------------------------- 1 | {% extends "videos/base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 | {% for video in videos %} 7 | {% cycle '
' '' '' '' %} 8 |
9 |
10 |
11 |
12 | 13 |
14 |
15 | 18 |
19 |
20 | {% cycle '' '' '' '
' %} 21 | {% endfor %} 22 |
23 |
24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/videos/templates/videos/upload.html: -------------------------------------------------------------------------------- 1 | {% extends "videos/base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 | 12 |
13 | {% if upload_success %} 14 |
15 | Uploading video file is successful! 16 |
17 | {% endif %} 18 |
19 | {% csrf_token %} 20 |
21 | 22 |
23 | 24 |
25 |
26 |
27 | 28 |
29 | 43 |
44 |
45 |
46 | 47 |
48 | 49 |
50 |
51 |
52 | 53 |
54 | 55 |
56 |
57 |
58 | 59 |
60 |
61 |
62 |
63 |
64 | 72 | {% endblock %} 73 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/videos/templates/videos/video.html: -------------------------------------------------------------------------------- 1 | {% extends "videos/base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 | 13 | 17 |
18 |
19 | 20 | {{ video.aggregate_likes }} 21 |
22 |
23 |
24 |
25 | {% csrf_token %} 26 | 27 | 28 |
29 | 30 |
31 | 32 |
33 |
34 |
35 | 36 |
37 | 38 |
39 |
40 |
41 | 42 |
43 |
44 |
45 |
46 |
47 | {% endblock %} 48 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/videos/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/videos/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.index, name='index'), 7 | path('channel/', views.channel, name='channel'), 8 | path('video//', views.video, name='video'), 9 | path('upload-video', views.upload, name='upload'), 10 | path('like-video', views.like, name='like'), 11 | ] 12 | -------------------------------------------------------------------------------- /Chapter12/decentralized_videos/videos/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from videos.models import videos_sharing 3 | 4 | 5 | def index(request): 6 | videos = videos_sharing.recent_videos() 7 | context = {'videos': videos} 8 | return render(request, 'videos/index.html', context) 9 | 10 | def channel(request, video_user): 11 | videos = videos_sharing.get_videos(video_user) 12 | context = {'videos': videos, 'video_user': video_user} 13 | return render(request, 'videos/channel.html', context) 14 | 15 | def video(request, video_user, index): 16 | video = videos_sharing.get_video(video_user, index) 17 | context = {'video': video} 18 | return render(request, 'videos/video.html', context) 19 | 20 | def upload(request): 21 | context = {} 22 | if request.POST: 23 | video_user = request.POST['video_user'] 24 | title = request.POST['title'] 25 | video_file = request.FILES['video_file'] 26 | password = request.POST['password'] 27 | videos_sharing.upload_video(video_user, password, video_file, title) 28 | context['upload_success'] = True 29 | return render(request, 'videos/upload.html', context) 30 | 31 | def like(request): 32 | video_user = request.POST['video_user'] 33 | index = int(request.POST['index']) 34 | password = request.POST['password'] 35 | video_liker = request.POST['video_liker'] 36 | videos_sharing.like_video(video_liker, password, video_user, index) 37 | return redirect('video', video_user=video_user, index=index) 38 | -------------------------------------------------------------------------------- /Chapter12/videos_sharing_smart_contract/contracts/VideosSharing.vy: -------------------------------------------------------------------------------- 1 | struct Video: 2 | path: bytes[50] 3 | title: bytes[20] 4 | 5 | Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256}) 6 | Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256}) 7 | UploadVideo: event({_user: indexed(address), _index: uint256}) 8 | LikeVideo: event({_video_liker: indexed(address), _video_uploader: indexed(address), _index: uint256}) 9 | 10 | user_videos_index: map(address, uint256) 11 | 12 | name: public(bytes[20]) 13 | symbol: public(bytes[3]) 14 | totalSupply: public(uint256) 15 | decimals: public(uint256) 16 | balances: map(address, uint256) 17 | allowed: map(address, map(address, uint256)) 18 | 19 | all_videos: map(address, map(uint256, Video)) 20 | 21 | likes_videos: map(bytes[100], bool) 22 | aggregate_likes: map(bytes[100], uint256) 23 | 24 | @public 25 | def __init__(): 26 | _initialSupply: uint256 = 500 27 | _decimals: uint256 = 3 28 | self.totalSupply = _initialSupply * 10 ** _decimals 29 | self.balances[msg.sender] = self.totalSupply 30 | self.name = 'Video Sharing Coin' 31 | self.symbol = 'VID' 32 | self.decimals = _decimals 33 | log.Transfer(ZERO_ADDRESS, msg.sender, self.totalSupply) 34 | 35 | @public 36 | @constant 37 | def balanceOf(_owner: address) -> uint256: 38 | return self.balances[_owner] 39 | 40 | @private 41 | def _transfer(_source: address, _to: address, _amount: uint256) -> bool: 42 | assert self.balances[_source] >= _amount 43 | self.balances[_source] -= _amount 44 | self.balances[_to] += _amount 45 | log.Transfer(_source, _to, _amount) 46 | 47 | return True 48 | 49 | @public 50 | def transfer(_to: address, _amount: uint256) -> bool: 51 | return self._transfer(msg.sender, _to, _amount) 52 | 53 | @public 54 | def transferFrom(_from: address, _to: address, _value: uint256) -> bool: 55 | assert _value <= self.allowed[_from][msg.sender] 56 | assert _value <= self.balances[_from] 57 | 58 | self.balances[_from] -= _value 59 | self.allowed[_from][msg.sender] -= _value 60 | self.balances[_to] += _value 61 | log.Transfer(_from, _to, _value) 62 | 63 | return True 64 | 65 | @public 66 | def approve(_spender: address, _amount: uint256) -> bool: 67 | self.allowed[msg.sender][_spender] = _amount 68 | log.Approval(msg.sender, _spender, _amount) 69 | 70 | return True 71 | 72 | @public 73 | @constant 74 | def allowance(_owner: address, _spender: address) -> uint256: 75 | return self.allowed[_owner][_spender] 76 | 77 | @public 78 | def upload_video(_video_path: bytes[50], _video_title: bytes[20]) -> bool: 79 | _index: uint256 = self.user_videos_index[msg.sender] 80 | 81 | self.all_videos[msg.sender][_index] = Video({ path: _video_path, title: _video_title }) 82 | self.user_videos_index[msg.sender] += 1 83 | 84 | log.UploadVideo(msg.sender, _index) 85 | 86 | return True 87 | 88 | @public 89 | @constant 90 | def latest_videos_index(_user: address) -> uint256: 91 | return self.user_videos_index[_user] 92 | 93 | @public 94 | @constant 95 | def videos_path(_user: address, _index: uint256) -> bytes[50]: 96 | return self.all_videos[_user][_index].path 97 | 98 | @public 99 | @constant 100 | def videos_title(_user: address, _index: uint256) -> bytes[20]: 101 | return self.all_videos[_user][_index].title 102 | 103 | @public 104 | def like_video(_user: address, _index: uint256) -> bool: 105 | _msg_sender_str: bytes32 = convert(msg.sender, bytes32) 106 | _user_str: bytes32 = convert(_user, bytes32) 107 | _index_str: bytes32 = convert(_index, bytes32) 108 | _key: bytes[100] = concat(_msg_sender_str, _user_str, _index_str) 109 | _likes_key: bytes[100] = concat(_user_str, _index_str) 110 | 111 | assert _index < self.user_videos_index[_user] 112 | assert self.likes_videos[_key] == False 113 | 114 | self.likes_videos[_key] = True 115 | self.aggregate_likes[_likes_key] += 1 116 | self._transfer(msg.sender, _user, 1) 117 | 118 | log.LikeVideo(msg.sender, _user, _index) 119 | 120 | return True 121 | 122 | @public 123 | @constant 124 | def video_has_been_liked(_user_like: address, _user_video: address, _index: uint256) -> bool: 125 | _user_like_str: bytes32 = convert(_user_like, bytes32) 126 | _user_video_str: bytes32 = convert(_user_video, bytes32) 127 | _index_str: bytes32 = convert(_index, bytes32) 128 | _key: bytes[100] = concat(_user_like_str, _user_video_str, _index_str) 129 | 130 | return self.likes_videos[_key] 131 | 132 | @public 133 | @constant 134 | def video_aggregate_likes(_user_video: address, _index: uint256) -> uint256: 135 | _user_video_str: bytes32 = convert(_user_video, bytes32) 136 | _index_str: bytes32 = convert(_index, bytes32) 137 | _key: bytes[100] = concat(_user_video_str, _index_str) 138 | 139 | return self.aggregate_likes[_key] 140 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Hands-On Blockchain for Python Developers 5 | 6 | Hands-On Blockchain for Python Developers 7 | 8 | This is the code repository for [Hands-On Blockchain for Python Developers](https://www.packtpub.com/big-data-and-business-intelligence/hands-blockchain-python-developers?utm_source=github&utm_medium=repository&utm_campaign=9781788627856), published by Packt. 9 | 10 | **Gain blockchain programming skills to build decentralized applications using Python** 11 | 12 | ## What is this book about? 13 | Blockchain is seen as the main technological solution that works as a public ledger for all cryptocurrency transactions. This book serves as a practical guide to developing a full-fledged decentralized application with Python to interact with the various building blocks of blockchain applications. 14 | 15 | This book covers the following exciting features: 16 | * Understand blockchain technology and what makes it an immutable database 17 | * Use the features of web3.py API to interact with the smart contract 18 | * Create your own cryptocurrency and token in Ethereum using Vyper 19 | * Use IPFS features to store content on the decentralized storage platform 20 | * Implement a Twitter-like decentralized application with a desktop frontend 21 | 22 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1788627857) today! 23 | 24 | https://www.packtpub.com/ 25 | 26 | 27 | ## Instructions and Navigations 28 | All of the code is organized into folders. For example, Chapter02. 29 | 30 | The code will look like the following: 31 | ``` 32 | "compilation": { 33 | "backend": { 34 | "class": "populus.compilation.backends.VyperBackend" 35 | }, 36 | "contract_source_dirs": [ 37 | "./contracts" 38 | ], 39 | "import_remappings": [] 40 | }, 41 | ``` 42 | 43 | **Following is what you need for this book:** 44 | If you are a Python developer who wants to enter the world of blockchain, Hands-On Blockchain for Python Developers is for you. The book will be your go-to guide to becoming well-versed with the blockchain ecosystem and building your own decentralized applications using Python and library support. 45 | 46 | With the following software and hardware list you can run all code files present in the book (Chapter 1-15). 47 | 48 | ### Software and Hardware List 49 | 50 | | Chapter | Software required | OS required | 51 | | -------- | ------------------------------------| -----------------------------------| 52 | | 1-12 | python 3.6, anaconda 5.3, Ganache and Truffle framework | Linux (Any) | 53 | 54 | 55 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](https://www.packtpub.com/sites/default/files/downloads/9781788627856_ColorImages.pdf). 56 | 57 | 58 | ### Related products 59 | * Blockchain By Example [[Packt]](https://www.packtpub.com/big-data-and-business-intelligence/blockchain-example?utm_source=github&utm_medium=repository&utm_campaign=9781788475686) [[Amazon]](https://www.amazon.com/dp/1788475682) 60 | * Foundations of Blockchain [[Packt]](https://www.packtpub.com/big-data-and-business-intelligence/foundations-blockchain?utm_source=github&utm_medium=repository&utm_campaign=9781789139396) [[Amazon]](https://www.amazon.com/dp/1789139392) 61 | 62 | ## Get to Know the Author 63 | **Arjuna Sky Kok** has experience more than 10 years in expressing himself as a software engineer. He has developed web applications using Symfony, Laravel, Ruby on Rails, and Django. He also has built mobile applications on top of Android and iOS platforms. Currently, he is researching Ethereum technology. Other than that, he teaches Android and iOS programming to students. 64 | He graduated from Bina Nusantara University with majors in Computer Science and Applied Mathematics. He always strives to become a holistic person by enjoying leisure activities, such as dancing Salsa, learning French, and playing StarCraft 2. He lives quietly 65 | in the bustling city of Jakarta. 66 | 67 | 68 | ### Suggestions and Feedback 69 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSdy7dATC6QmEL81FIUuymZ0Wy9vH1jHkvpY57OiMeKGqib_Ow/viewform) if you have any feedback or suggestions. 70 | ### Download a free PDF 71 | 72 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
73 |

https://packt.link/free-ebook/9781788627856

--------------------------------------------------------------------------------