def build_evm_message(self, transaction): transaction_context = self.get_transaction_context(transaction) gas_fee = transaction.gas * transaction_context.gas_price # Buy Gas self.vm_state.account_db.delta_balance(transaction.sender, -1 * gas_fee) # Increment Nonce self.vm_state.account_db.increment_nonce(transaction.sender) # Setup VM Message message_gas = transaction.gas - transaction.intrinsic_gas if transaction.to == constants.CREATE_CONTRACT_ADDRESS: contract_address = generate_contract_address( transaction.sender, self.vm_state.account_db.get_nonce(transaction.sender) - 1, ) data = b'' code = transaction.data else: contract_address = None data = transaction.data code = self.vm_state.account_db.get_code(transaction.to) self.vm_state.logger.debug( ( "TRANSACTION: sender: %s | to: %s | value: %s | gas: %s | " "gas-price: %s | s: %s | r: %s | v: %s | data-hash: %s" ), encode_hex(transaction.sender), encode_hex(transaction.to), transaction.value, transaction.gas, transaction.gas_price, transaction.s, transaction.r, transaction.v, encode_hex(keccak(transaction.data)), ) message = Message( gas=message_gas, to=transaction.to, sender=transaction.sender, value=transaction.value, data=data, code=code, create_address=contract_address, ) return message
def receipt_to_dict(receipt: Receipt, tx_hash: Hash32, chain: AsyncChain) -> Dict[str, str]: dict_to_return = all_rlp_fields_to_dict_camel_case(receipt) block_hash, index, is_receive = chain.chaindb.get_transaction_index( tx_hash) dict_to_return['blockHash'] = to_hex(block_hash) dict_to_return['transactionHash'] = to_hex(tx_hash) dict_to_return['isReceive'] = to_hex(is_receive) dict_to_return['transactionIndex'] = to_hex(index) block_header = chain.get_block_header_by_hash(block_hash) dict_to_return['blockNumber'] = to_hex(block_header.block_number) for i in range(len(dict_to_return['logs'])): dict_to_return['logs'][i]['logIndex'] = to_hex(i) dict_to_return['logs'][i]['transactionIndex'] = to_hex(index) dict_to_return['logs'][i]['transactionHash'] = to_hex(tx_hash) dict_to_return['logs'][i]['blockHash'] = to_hex(block_hash) dict_to_return['logs'][i]['blockNumber'] = to_hex( block_header.block_number) dict_to_return['logs'][i]['topics'] = [ pad_hex(value, 32) for value in dict_to_return['logs'][i]['topics'] ] transaction = chain.get_canonical_transaction(tx_hash) if is_receive: dict_to_return['to'] = to_hex(block_header.chain_address) dict_to_return['sender'] = to_hex( chain.chaindb.get_chain_wallet_address_for_block_hash( transaction.sender_block_hash)) else: dict_to_return['to'] = to_hex(transaction.to) dict_to_return['sender'] = to_hex(transaction.sender) if transaction.to == CREATE_CONTRACT_ADDRESS: dict_to_return['contractAddress'] = to_hex( generate_contract_address(transaction.sender, transaction.nonce)) dict_to_return['cumulativeGasUsed'] = to_hex( chain.chaindb.get_cumulative_gas_used(tx_hash)) return dict_to_return
def __call__(self, computation): computation.consume_gas(self.gas_cost, reason=self.mnemonic) value, start_position, size = computation.stack_pop( num_items=3, type_hint=constants.UINT256, ) computation.extend_memory(start_position, size) insufficient_funds = computation.state.account_db.get_balance( computation.msg.storage_address) < value stack_too_deep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT if insufficient_funds or stack_too_deep: computation.stack_push(0) return call_data = computation.memory_read(start_position, size) create_msg_gas = self.max_child_gas_modifier( computation.get_gas_remaining()) computation.consume_gas(create_msg_gas, reason="CREATE") creation_nonce = computation.state.account_db.get_nonce( computation.msg.storage_address) computation.state.account_db.increment_nonce( computation.msg.storage_address) contract_address = generate_contract_address( computation.msg.storage_address, creation_nonce, ) is_collision = computation.state.account_db.account_has_code_or_nonce( contract_address) if is_collision: self.logger.debug( "Address collision while creating contract: %s", encode_hex(contract_address), ) computation.stack_push(0) return child_msg = computation.prepare_child_message( gas=create_msg_gas, to=constants.CREATE_CONTRACT_ADDRESS, value=value, data=b'', code=call_data, create_address=contract_address, ) child_computation = computation.apply_child_computation(child_msg) if child_computation.is_error: computation.stack_push(0) else: computation.stack_push(contract_address) computation.return_gas(child_computation.get_gas_remaining())
def build_evm_message( self, send_transaction: BaseTransaction, transaction_context: BaseTransactionContext, receive_transaction: BaseReceiveTransaction = None) -> Message: if transaction_context.is_refund == True: # Setup VM Message message_gas = 0 refund_amount = receive_transaction.remaining_refund contract_address = None data = b'' code = b'' self.vm_state.logger.debug( ("REFUND TRANSACTION: sender: %s | refund amount: %s "), encode_hex(send_transaction.sender), refund_amount, ) elif transaction_context.is_receive == True: # this is a receive transaction - now we get to execute any code or data # transaction_context = self.get_transaction_context(send_transaction) # gas_fee = transaction.transaction.gas * transaction_context.gas_price # TODO: # fail niceley here so we can put a failed tx. the failed tx can be seen in the receipt status_code # we will have to refund the sender the money if this is the case. # so the amount of gas the send tx paid is saved as transaction.transaction.gas # Setup VM Message # message_gas = transaction.transaction.gas - transaction.transaction.intrinsic_gas -1 * gas_fee # I tested this, if this tx uses more gas than what was charged to the send tx it will fail. # Setup VM Message message_gas = send_transaction.gas - send_transaction.intrinsic_gas refund_amount = 0 if send_transaction.to == constants.CREATE_CONTRACT_ADDRESS: # the contract address was already chosen on the send transaction. It is now the caller chain address contract_address = transaction_context.caller_chain_address data = b'' code = send_transaction.data else: contract_address = None data = send_transaction.data code = self.vm_state.account_db.get_code(send_transaction.to) self.vm_state.logger.debug( ("RECEIVE TRANSACTION: hash: %s | sender: %s | to: %s | value: %s | gas: %s | " "gas-price: %s | s: %s | r: %s | v: %s | data-hash: %s"), encode_hex(send_transaction.hash), encode_hex(send_transaction.sender), encode_hex(send_transaction.to), send_transaction.value, send_transaction.gas, send_transaction.gas_price, send_transaction.s, send_transaction.r, send_transaction.v, encode_hex(keccak(data)), ) else: # this is a send transaction #transaction_context = self.get_transaction_context(send_transaction, receive_transaction) gas_fee = send_transaction.gas * transaction_context.gas_price #this is the default gas fee for the send tx that needs to be subtracted on the receive of a smart contract # Buy Gas self.vm_state.account_db.delta_balance(send_transaction.sender, -1 * gas_fee) # Increment Nonce self.vm_state.account_db.increment_nonce(send_transaction.sender) # Setup VM Message message_gas = send_transaction.gas - send_transaction.intrinsic_gas refund_amount = 0 #when a contract is created with a send transaction, do no computation. #we have to put the computation back. because it needs to charge computation #gas on the send. We just have to make sure it doesnt execute the transaction... #TODO: make sure the computation is not executed #temporarily we will just do no computation. This means interactions with #smart contracts will cost no gas until we finish this. if send_transaction.to == constants.CREATE_CONTRACT_ADDRESS: contract_address = generate_contract_address( send_transaction.sender, self.vm_state.account_db.get_nonce(send_transaction.sender) - 1, ) data = b'' code = send_transaction.data else: contract_address = None data = send_transaction.data code = self.vm_state.account_db.get_code(send_transaction.to) self.vm_state.logger.debug( ("SEND TRANSACTION: sender: %s | to: %s | value: %s | gas: %s | " "gas-price: %s | s: %s | r: %s | v: %s | data-hash: %s"), encode_hex(send_transaction.sender), encode_hex(send_transaction.to), send_transaction.value, send_transaction.gas, send_transaction.gas_price, send_transaction.s, send_transaction.r, send_transaction.v, encode_hex(keccak(send_transaction.data)), ) message = Message( gas=message_gas, to=send_transaction.to, sender=send_transaction.sender, value=send_transaction.value, data=data, code=code, create_address=contract_address, refund_amount=refund_amount, ) return message
def test_erc_20_smart_contract_deploy_system(): # testdb = LevelDB('/home/tommy/.local/share/helios/instance_test/mainnet/chain/full/') # testdb = JournalDB(testdb) testdb = MemoryDB() chain = TestnetChain( testdb, TESTNET_GENESIS_PRIVATE_KEY.public_key.to_canonical_address(), TESTNET_GENESIS_PRIVATE_KEY) coin_mature_time = chain.get_vm(timestamp=Timestamp(int( time.time()))).consensus_db.coin_mature_time_for_staking now = int(time.time()) key_balance_dict = { private_keys[0]: (1000000000000, now - coin_mature_time * 10 - 100) } create_dev_fixed_blockchain_database(testdb, key_balance_dict) chain = TestnetChain( testdb, TESTNET_GENESIS_PRIVATE_KEY.public_key.to_canonical_address(), TESTNET_GENESIS_PRIVATE_KEY) min_time_between_blocks = chain.get_vm( timestamp=Timestamp(int(time.time()))).min_time_between_blocks for private_key, balance_time in key_balance_dict.items(): assert (chain.get_vm().state.account_db.get_balance( private_key.public_key.to_canonical_address()) == balance_time[0]) SOLIDITY_SRC_FILE = 'contract_data/erc20.sol' EXPECTED_TOTAL_SUPPLY = 10000000000000000000000 #compiled_sol = compile_files([SOLIDITY_SRC_FILE]) compiled_sol = load_compiled_sol_dict('contract_data/erc20_compiled.pkl') contract_interface = compiled_sol['{}:SimpleToken'.format( SOLIDITY_SRC_FILE)] w3 = Web3() SimpleToken = w3.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin']) # Build transaction to deploy the contract w3_tx1 = SimpleToken.constructor().buildTransaction(W3_TX_DEFAULTS) max_gas = 20000000 chain.create_and_sign_transaction_for_queue_block( gas_price=0x01, gas=max_gas, to=CREATE_CONTRACT_ADDRESS, value=0, data=decode_hex(w3_tx1['data']), v=0, r=0, s=0) #time.sleep(1) print("deploying smart contract") initial_balance = chain.get_vm().state.account_db.get_balance( TESTNET_GENESIS_PRIVATE_KEY.public_key.to_canonical_address()) imported_block = chain.import_current_queue_block() final_balance = chain.get_vm().state.account_db.get_balance( TESTNET_GENESIS_PRIVATE_KEY.public_key.to_canonical_address()) gas_used = to_int( chain.chaindb.get_receipts(imported_block.header, Receipt)[0].gas_used) assert ((initial_balance - final_balance) == gas_used) print(TESTNET_GENESIS_PRIVATE_KEY.public_key.to_canonical_address()) print( generate_contract_address( TESTNET_GENESIS_PRIVATE_KEY.public_key.to_canonical_address(), imported_block.transactions[0].nonce)) print( chain.chaindb.get_receipts(imported_block.header, Receipt)[0].logs[0].address) #contractAddress print("Used the correct amount of gas.") #now we need to add the block to the smart contract list_of_smart_contracts = chain.get_vm( ).state.account_db.get_smart_contracts_with_pending_transactions() deployed_contract_address = list_of_smart_contracts[0] print(list_of_smart_contracts) chain = TestnetChain(testdb, deployed_contract_address, private_keys[0]) chain.populate_queue_block_with_receive_tx() imported_block = chain.import_current_queue_block() list_of_smart_contracts = chain.get_vm( ).state.account_db.get_smart_contracts_with_pending_transactions() print(list_of_smart_contracts) #lets make sure it didn't create a refund transaction for the initial sender. print(chain.get_vm().state.account_db.has_receivable_transactions( TESTNET_GENESIS_PRIVATE_KEY.public_key.to_canonical_address())) # print('ASDASD') # print(chain.chaindb.get_receipts(imported_block.header, Receipt)[0].logs[0].data) # # Interacting with deployed smart contract step 1) add send transaction # chain = TestnetChain( testdb, TESTNET_GENESIS_PRIVATE_KEY.public_key.to_canonical_address(), TESTNET_GENESIS_PRIVATE_KEY) simple_token = w3.eth.contract( address=Web3.toChecksumAddress(deployed_contract_address), abi=contract_interface['abi'], ) w3_tx2 = simple_token.functions.totalSupply().buildTransaction( W3_TX_DEFAULTS) chain.create_and_sign_transaction_for_queue_block( gas_price=0x01, gas=max_gas, to=deployed_contract_address, value=0, data=decode_hex(w3_tx2['data']), v=0, r=0, s=0) #lets make sure it subtracts the entire max gas initial_balance = chain.get_vm().state.account_db.get_balance( TESTNET_GENESIS_PRIVATE_KEY.public_key.to_canonical_address()) print("waiting {} seconds before importing next block".format( min_time_between_blocks)) time.sleep(min_time_between_blocks) chain.import_current_queue_block() final_balance = chain.get_vm().state.account_db.get_balance( TESTNET_GENESIS_PRIVATE_KEY.public_key.to_canonical_address()) assert ((initial_balance - final_balance) == max_gas) # # Interacting with deployed smart contract step 2) add receive transaction to smart contract chain # chain = TestnetChain(testdb, deployed_contract_address, private_keys[0]) chain.populate_queue_block_with_receive_tx() receivable_transactions = chain.get_vm( ).state.account_db.get_receivable_transactions(deployed_contract_address) print('receivable_transactions before imported into contract chain') print(receivable_transactions) print("waiting {} seconds before importing next block".format( min_time_between_blocks)) time.sleep(min_time_between_blocks) imported_block = chain.import_current_queue_block() receipt = chain.chaindb.get_receipts(imported_block.header, Receipt)[0] receipt_dict = format_receipt_for_web3_to_extract_events( receipt, imported_block.receive_transactions[0].hash, chain) rich_logs = simple_token.events.Print().processReceipt(receipt_dict) print(rich_logs[0]['args']) print('a') #now lets look at the reciept to see the result assert (to_int( chain.chaindb.get_receipts( imported_block.header, Receipt)[0].logs[0].data) == EXPECTED_TOTAL_SUPPLY) print("Total supply call gave expected result!") gas_used = to_int( chain.chaindb.get_receipts(imported_block.header, Receipt)[0].gas_used) # # Interacting with deployed smart contract step 3) Receiving refund of extra gas that wasn't used in the computation # initial_balance = chain.get_vm().state.account_db.get_balance( TESTNET_GENESIS_PRIVATE_KEY.public_key.to_canonical_address()) chain = TestnetChain( testdb, TESTNET_GENESIS_PRIVATE_KEY.public_key.to_canonical_address(), TESTNET_GENESIS_PRIVATE_KEY) # # Make sure the receive transaction is no longer in the account receivable # receivable_transactions = chain.get_vm( ).state.account_db.get_receivable_transactions(deployed_contract_address) print('receivable_transactions after imported into contract chain') print(receivable_transactions) chain.populate_queue_block_with_receive_tx() print("waiting {} seconds before importing next block".format( min_time_between_blocks)) time.sleep(min_time_between_blocks) imported_block = chain.import_current_queue_block() final_balance = chain.get_vm().state.account_db.get_balance( TESTNET_GENESIS_PRIVATE_KEY.public_key.to_canonical_address()) assert ((final_balance - initial_balance) == (max_gas - gas_used)) print("Refunded gas is the expected amount.")