예제 #1
0
    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
예제 #2
0
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
예제 #3
0
    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())
예제 #4
0
    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.")