// calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for // the operation. This does not reduce gas or resizes the memory. func calculateGasAndSize(env Environment, context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) { var ( gas = new(big.Int) newMemSize *big.Int = new(big.Int) ) err := baseCheck(op, stack, gas) if err != nil { return nil, nil, err } // stack Check, memory resize & gas phase switch op { case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: n := int(op - SWAP1 + 2) err := stack.require(n) if err != nil { return nil, nil, err } gas.Set(GasFastestStep) case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: n := int(op - DUP1 + 1) err := stack.require(n) if err != nil { return nil, nil, err } gas.Set(GasFastestStep) case LOG0, LOG1, LOG2, LOG3, LOG4: n := int(op - LOG0) err := stack.require(n + 2) if err != nil { return nil, nil, err } mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1] gas.Add(gas, params.LogGas) gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(n)), params.LogTopicGas)) gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas)) newMemSize = calcMemSize(mStart, mSize) case EXP: gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas)) case SSTORE: err := stack.require(2) if err != nil { return nil, nil, err } var g *big.Int y, x := stack.data[stack.len()-2], stack.data[stack.len()-1] val := statedb.GetState(context.Address(), common.BigToHash(x)) // This checks for 3 scenario's and calculates gas accordingly // 1. From a zero-value address to a non-zero value (NEW VALUE) // 2. From a non-zero value address to a zero-value address (DELETE) // 3. From a nen-zero to a non-zero (CHANGE) if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) { // 0 => non 0 g = params.SstoreSetGas } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) { statedb.Refund(params.SstoreRefundGas) g = params.SstoreClearGas } else { // non 0 => non 0 (or 0 => 0) g = params.SstoreClearGas } gas.Set(g) case SUICIDE: if !statedb.IsDeleted(context.Address()) { statedb.Refund(params.SuicideRefundGas) } case MLOAD: newMemSize = calcMemSize(stack.peek(), u256(32)) case MSTORE8: newMemSize = calcMemSize(stack.peek(), u256(1)) case MSTORE: newMemSize = calcMemSize(stack.peek(), u256(32)) case RETURN: newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) case SHA3: newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) words := toWordSize(stack.data[stack.len()-2]) gas.Add(gas, words.Mul(words, params.Sha3WordGas)) case CALLDATACOPY: newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) words := toWordSize(stack.data[stack.len()-3]) gas.Add(gas, words.Mul(words, params.CopyGas)) case CODECOPY: newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) words := toWordSize(stack.data[stack.len()-3]) gas.Add(gas, words.Mul(words, params.CopyGas)) case EXTCODECOPY: newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4]) words := toWordSize(stack.data[stack.len()-4]) gas.Add(gas, words.Mul(words, params.CopyGas)) case CREATE: newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3]) case CALL, CALLCODE: gas.Add(gas, stack.data[stack.len()-1]) if op == CALL { if env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil { gas.Add(gas, params.CallNewAccountGas) } } if len(stack.data[stack.len()-3].Bytes()) > 0 { gas.Add(gas, params.CallValueTransferGas) } x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7]) y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5]) newMemSize = common.BigMax(x, y) } if newMemSize.Cmp(common.Big0) > 0 { newMemSizeWords := toWordSize(newMemSize) newMemSize.Mul(newMemSizeWords, u256(32)) if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 { oldSize := toWordSize(big.NewInt(int64(mem.Len()))) pow := new(big.Int).Exp(oldSize, common.Big2, Zero) linCoef := new(big.Int).Mul(oldSize, params.MemoryGas) quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv) oldTotalFee := new(big.Int).Add(linCoef, quadCoef) pow.Exp(newMemSizeWords, common.Big2, Zero) linCoef = new(big.Int).Mul(newMemSizeWords, params.MemoryGas) quadCoef = new(big.Int).Div(pow, params.QuadCoeffDiv) newTotalFee := new(big.Int).Add(linCoef, quadCoef) fee := new(big.Int).Sub(newTotalFee, oldTotalFee) gas.Add(gas, fee) } } return newMemSize, gas, nil }