core-state-process source code analysis for Ethernet workshop in Brotherhood Block Chain Tutorial (2)

Keywords: Go Blockchain Database

Brotherhood Block Chain Tutorial Etaifang Source Code Analysis core-state-process Source Code Analysis (II):
The calculation of g0 is introduced in detail in the Yellow paper.
Part of the difference from the yellow paper is if contractCreation & & homestead {igas. SetUint64 (params. TxGasContractCreation) because Gtxcreate+Gtransaction = TxGasContractCreation

func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
    igas := new(big.Int)
    if contractCreation && homestead {
        igas.SetUint64(params.TxGasContractCreation)
    } else {
        igas.SetUint64(params.TxGas)
    }
    if len(data) > 0 {
        var nz int64
        for _, byt := range data {
            if byt != 0 {
                nz++
            }
        }
        m := big.NewInt(nz)
        m.Mul(m, new(big.Int).SetUint64(params.TxDataNonZeroGas))
        igas.Add(igas, m)
        m.SetInt64(int64(len(data)) - nz)
        m.Mul(m, new(big.Int).SetUint64(params.TxDataZeroGas))
        igas.Add(igas, m)
    }
    return igas
}

Pre-Execution Inspection
func (st *StateTransition) preCheck() error {

msg := st.msg
sender := st.from()

// Make sure this transaction's nonce is correct
if msg.CheckNonce() {
    nonce := st.state.GetNonce(sender.Address())
    // The current local Nonce needs to be the same as msg's one, otherwise the state is out of sync.
    if nonce < msg.Nonce() {
        return ErrNonceTooHigh
    } else if nonce > msg.Nonce() {
        return ErrNonceTooLow
    }
}
return st.buyGas()

}
Buy Gas, realize Gas's pre-deduction fee, deduct your GasLimit* GasPrice money first. Then a portion is returned based on the calculated state.
func (st *StateTransition) buyGas() error {

mgas := st.msg.Gas()
if mgas.BitLen() > 64 {
    return vm.ErrOutOfGas
}

mgval := new(big.Int).Mul(mgas, st.gasPrice)

var (
    state = st.state
    sender = st.from()
)
if state.GetBalance(sender.Address()).Cmp(mgval) < 0 {
    return errInsufficientBalanceForGas
}
if err := st.gp.SubGas(mgas); err != nil { // Subtract it from the gaspool of the block, because the block is restricted by GasLimit to Gas for the whole block.
    return err
}
st.gas += mgas.Uint64()

st.initialGas.Set(mgas)
state.SubBalance(sender.Address(), mgval)
// Subtract GasLimit* GasPrice from the account
return nil

}

Tax rebates are designed to reward people to run instructions that reduce the burden of block chains, such as clearing the storage of accounts or running suicide commands to clear accounts.

func (st *StateTransition) refundGas() {

// Return eth for remaining gas to the sender account,
// exchanged at the original rate.
sender := st.from() // err already checked
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
// First, return the remaining Gas from the user.
st.state.AddBalance(sender.Address(), remaining)

// Apply refund counter, capped to half of the used gas.
// Then the total amount of tax rebate will not exceed one-second of the total amount used by Gas users.
uhalf := remaining.Div(st.gasUsed(), common.Big2)
refund := math.BigMin(uhalf, st.state.GetRefund())
st.gas += refund.Uint64()
// Add the amount of tax refund to the user account.
st.state.AddBalance(sender.Address(), refund.Mul(refund, st.gasPrice))

// Also return remaining gas to the block gas counter so it is
// available for the next transaction.
// At the same time, the refund money is returned to gaspool to make room for Gas in the next transaction.
st.gp.AddGas(new(big.Int).SetUint64(st.gas))

}

StateProcessor

StateTransition is used to handle transactions one by one. StateProcessor is then used to handle block-level transactions.

Structure and structure

// StateProcessor is a basic Processor, which takes care of transitioning
// state from one point to another.
//
// StateProcessor implements Processor.
type StateProcessor struct {

config *params.ChainConfig // Chain configuration options
bc *BlockChain // Canonical block chain
engine consensus.Engine // Consensus engine used for block rewards

}

// NewStateProcessor initialises a new StateProcessor.
func NewStateProcessor(config params.ChainConfig, bc BlockChain, engine consensus.Engine) *StateProcessor {

return &StateProcessor{
    config: config,
    bc: bc,
    engine: engine,
}

}

Process, this method is called by blockchain.


// Process processes the state changes according to the Ethereum rules by running
// the transaction messages using the statedb and applying any rewards to both
// the processor (coinbase) and any included uncles.
// Process runs the transaction information according to the ETF rules to change the state of statedb and reward the miner or other uncle nodes.
// Process returns the receipts and logs accumulated during the process and
// returns the amount of gas that was used in the process. If any of the
// transactions failed to execute due to insufficient gas it will return an error.
// Process returns the accumulated receipts and logs during execution, and Gas used during execution. If any transaction fails due to insufficient Gas, an error will be returned.
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, *big.Int, error) {
    var (
        receipts types.Receipts
        totalUsedGas = big.NewInt(0)
        header = block.Header()
        allLogs []*types.Log
        gp = new(GasPool).AddGas(block.GasLimit())
    )
    // Mutate the the block and state according to any hard-fork specs
    // Hard Bifurcation Processing of DAO Events
    if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
        misc.ApplyDAOHardFork(statedb)
    }
    // Iterate over and process the individual transactions
    for i, tx := range block.Transactions() {
        statedb.Prepare(tx.Hash(), block.Hash(), i)
        receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, totalUsedGas, cfg)
        if err != nil {
            return nil, nil, nil, err
        }
        receipts = append(receipts, receipt)
        allLogs = append(allLogs, receipt.Logs...)
    }
    // Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
    p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), receipts)
    // Return the total Gas usage and nil in the receipt log
    return receipts, allLogs, totalUsedGas, nil
}

ApplyTransaction


// ApplyTransaction attempts to apply a transaction to the given state database
// and uses the input parameters for its environment. It returns the receipt
// for the transaction, gas used and an error if the transaction failed,
// indicating the block was invalid.
ApplyTransaction Attempts are made to apply transactions to a given state database and to use the input parameters of its environment.
//It returns the receipt of the transaction, uses Gas and errors, and indicates that the block is invalid if the transaction fails.

func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *big.Int, error) {
    // Converting Transactions into Message s
    // Here's how to verify that the message was actually sent by Sender. TODO
    msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
    if err != nil {
        return nil, nil, err
    }
    // Create a new context to be used in the EVM environment
    // Each transaction creates a new virtual machine environment.
    context := NewEVMContext(msg, header, bc, author)
    // Create a new environment which holds all relevant information
    // about the transaction and calling mechanisms.
    vmenv := vm.NewEVM(context, statedb, config, cfg)
    // Apply the transaction to the current state (included in the env)
    _, gas, failed, err := ApplyMessage(vmenv, msg, gp)
    if err != nil {
        return nil, nil, err
    }

    // Update the state with pending changes
    // Find the Intermediate State
    var root []byte
    if config.IsByzantium(header.Number) {
        statedb.Finalise(true)
    } else {
        root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
    }
    usedGas.Add(usedGas, gas)

    // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
    // based on the eip phase, we're passing wether the root touch-delete accounts.
    // Create a receipt to store the root of the intermediate state and the gas used for the transaction
    receipt := types.NewReceipt(root, failed, usedGas)
    receipt.TxHash = tx.Hash()
    receipt.GasUsed = new(big.Int).Set(gas)
    // if the transaction created a contract, store the creation address in the receipt.
    // If it's a transaction that creates a contract, then we store the creation address in the receipt.
    if msg.To() == nil {
        receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
    }

    // Set the receipt logs and create a bloom for filtering
    receipt.Logs = statedb.GetLogs(tx.Hash())
    receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
    // Get all the logs and create a blog filter for the logs.
    return receipt, gas, err
}

Posted by t_galan on Sun, 27 Jan 2019 14:03:14 -0800