2

In Bitcoin, one has to refer to the UTXO as input,if you want to spend bitcoins. There are no accounts or a world state. Since Ethereum uses accountmodel, does one refer to the state transition function if one wants to spend Ethers? If Alice wants to send 5 Ether to Bob, she has to prove, that she has at least 5 Ether. Does she do this by refering to the state transition function? How does one know that Alice has these 5 Ethers?

Or does the state transition function come into play when a transation already is executed. How does the state transition function work in Ethereum, when Alice wants so send 5 Ether to Bob ? I read the white and yellow paper and did not get a proper answer for my question.

5

In Ethereum the world state is stored explicitly. It means, that every (full or archive) node in the network has a local copy of the state. The state is a (partial) mapping from addresses (i.e. 20 bytes number) to account states (that is a 4-tuple which specifies the nonce, the balance, the storage, and the code).

Let's suppose Alice signs and "spread" a transaction that transfers x Ether from her balance to the one of Bob. Whenever a node of the network receives this transaction it is put in a queue (memory pool). Possibly, this tx is included in a block by the miner and executed (each node in the network re-execute the transaction to verify that the miner is not cheating).

To execute the transaction, the following algorithm is used (it is a simplified version taken from the yellow paper):

  1. Some validity checks are performed
  2. Alice's nonce is incremented by 1 and the gas allocated for the transaction is removed from Alice's balance.
  3. Now if the transaction specifies a receiver (in our case Bob) we are dealing with a Message call (case a), otherwise with a contract creation (case b): a. Execute the message call b. Execute the contract creation
  4. If the execution of point 3 was successful, the changes to the state are kept, otherwise, we return back to the changes before point 3.
  5. The refund balance is returned to the sender and the used gas is given to the beneficiary address (i.e. the miner's one).

Let's analyze the algorithm for point 3a the message call. The algorithm is the following:

I. Transfer the amount of ether (x) from Alice's to Bob's account

II. Check if the receiver has an associated contract code. If it is not the case stop, the message call was successful.

III. If it has associated code the contract code is executed:

IV. If the execution was successful, keep the state, otherwise revert to the state prior to step I.

The algorithm work because everybody has a local copy of the explicit world state.

  • Thank you very much for your answer. So the world state contains all necessary information of every single account participating in Ethereum. Moreover, it contains the current balance of an account, right ? – Blnpwr Sep 11 '18 at 15:53
  • Yes, indeed, I refer to this answer to get a better insight about the world state. ethereum.stackexchange.com/questions/268/… – Briomkez Sep 11 '18 at 15:56
3

There is a file in Ethereum source, called core/state_transition.go

The function that does state transition is called transitionDb(), here it is:

// TransitionDb will transition the state by applying the current message and
// returning the result including the the used gas. It returns an error if it
// failed. An error indicates a consensus issue.
func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) {
    if err = st.preCheck(); err != nil {
        return
    }
    msg := st.msg
    sender := vm.AccountRef(msg.From())
    homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber)
    contractCreation := msg.To() == nil

    // Pay intrinsic gas
    gas, err := IntrinsicGas(st.data, contractCreation, homestead)
    if err != nil {
        return nil, 0, false, err
    }
    if err = st.useGas(gas); err != nil {
        return nil, 0, false, err
    }

    var (
        evm = st.evm
        // vm errors do not effect consensus and are therefor
        // not assigned to err, except for insufficient balance
        // error.
        vmerr error
    )
    if contractCreation {
        ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
    } else {
        // Increment the nonce for the next transaction
        st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
        ret, st.gas, vmerr = evm.Call(sender, st.to(), st.data, st.gas, st.value)
    }
    if vmerr != nil {
        log.Debug("VM returned with error", "err", vmerr)
        // The only possible consensus-error would be if there wasn't
        // sufficient balance to make the transfer happen. The first
        // balance transfer may never fail.
        if vmerr == vm.ErrInsufficientBalance {
            return nil, 0, false, vmerr
        }
    }
    st.refundGas()
    st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))

    return ret, st.gasUsed(), vmerr != nil, err
}

The pseudocode goes like this:

  1. buy gas, i.e. discount gasLimit*gasPrice from sender's account
  2. If destination address is zero, create a contract, otherwise, call an evm function
  3. Before any call or create a transfer is made from the senders account to the destination account for the amount of value
  4. the EVM is executed, if code is null (human account), it does nothing, otherwise execute contract's code
  5. If there is any storage left, refund 50% of used gas as maximum
  6. If there is any leftover gas, return it to the sender

State transition function is the transaction process itself, so, not before, not after, but during

That's basically it, if you have any question, ask in comment section.

Additional functions:

func (st *StateTransition) buyGas() error {
    mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
    if st.state.GetBalance(st.msg.From()).Cmp(mgval) < 0 {
        return errInsufficientBalanceForGas
    }
    if err := st.gp.SubGas(st.msg.Gas()); err != nil {
        return err
    }
    st.gas += st.msg.Gas()

    st.initialGas = st.msg.Gas()
    st.state.SubBalance(st.msg.From(), mgval)
    return nil
}

func (st *StateTransition) preCheck() error {
    // Make sure this transaction's nonce is correct.
    if st.msg.CheckNonce() {
        nonce := st.state.GetNonce(st.msg.From())
        if nonce < st.msg.Nonce() {
            return ErrNonceTooHigh
        } else if nonce > st.msg.Nonce() {
            return ErrNonceTooLow
        }
    }
    return st.buyGas()
}
  • Thank you, but what happens, if the sender does not enough Ether? Where is the reference to the senders account balance? How does one know if the account balance is sufficient? – Blnpwr Sep 11 '18 at 15:48
  • checking source balance goes in the preCheck() function – Nulik Sep 11 '18 at 15:49
  • Could you explain this function like you did just before ? – Blnpwr Sep 11 '18 at 15:50
  • @Blnpwr, I have added the sources for these validations – Nulik Sep 11 '18 at 15:52
  • The precheck function only checks if the nonce is correct. But there is not any checkfunction that checks if the sender has enough ether on his account? – Blnpwr Sep 11 '18 at 15:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.