github.com/core-coin/go-core/v2@v2.1.9/core/vm/energy_table.go (about)

     1  // Copyright 2023 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-core library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package vm
    18  
    19  import (
    20  	"errors"
    21  
    22  	"github.com/core-coin/go-core/v2/common"
    23  	"github.com/core-coin/go-core/v2/common/math"
    24  	"github.com/core-coin/go-core/v2/params"
    25  )
    26  
    27  // memoryEnergyCost calculates the quadratic energy for memory expansion. It does so
    28  // only for the memory region that is expanded, not the total memory.
    29  func memoryEnergyCost(mem *Memory, newMemSize uint64) (uint64, error) {
    30  	if newMemSize == 0 {
    31  		return 0, nil
    32  	}
    33  	// The maximum that will fit in a uint64 is max_word_count - 1. Anything above
    34  	// that will result in an overflow. Additionally, a newMemSize which results in
    35  	// a newMemSizeWords larger than 0xFFFFFFFF will cause the square operation to
    36  	// overflow. The constant 0x1FFFFFFFE0 is the highest number that can be used
    37  	// without overflowing the energy calculation.
    38  	if newMemSize > 0x1FFFFFFFE0 {
    39  		return 0, ErrEnergyUintOverflow
    40  	}
    41  	newMemSizeWords := toWordSize(newMemSize)
    42  	newMemSize = newMemSizeWords * 32
    43  
    44  	if newMemSize > uint64(mem.Len()) {
    45  		square := newMemSizeWords * newMemSizeWords
    46  		linCoef := newMemSizeWords * params.MemoryEnergy
    47  		quadCoef := square / params.QuadCoeffDiv
    48  		newTotalFee := linCoef + quadCoef
    49  
    50  		fee := newTotalFee - mem.lastEnergyCost
    51  		mem.lastEnergyCost = newTotalFee
    52  
    53  		return fee, nil
    54  	}
    55  	return 0, nil
    56  }
    57  
    58  // memoryCopierEnergy creates the energy functions for the following opcodes, and takes
    59  // the stack position of the operand which determines the size of the data to copy
    60  // as argument:
    61  // CALLDATACOPY (stack position 2)
    62  // CODECOPY (stack position 2)
    63  // EXTCODECOPY (stack poition 3)
    64  // RETURNDATACOPY (stack position 2)
    65  func memoryCopierEnergy(stackpos int) energyFunc {
    66  	return func(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
    67  		// Energy for expanding the memory
    68  		energy, err := memoryEnergyCost(mem, memorySize)
    69  		if err != nil {
    70  			return 0, err
    71  		}
    72  		// And energy for copying data, charged per word at param.CopyEnergy
    73  		words, overflow := stack.Back(stackpos).Uint64WithOverflow()
    74  		if overflow {
    75  			return 0, ErrEnergyUintOverflow
    76  		}
    77  
    78  		if words, overflow = math.SafeMul(toWordSize(words), params.CopyEnergy); overflow {
    79  			return 0, ErrEnergyUintOverflow
    80  		}
    81  
    82  		if energy, overflow = math.SafeAdd(energy, words); overflow {
    83  			return 0, ErrEnergyUintOverflow
    84  		}
    85  		return energy, nil
    86  	}
    87  }
    88  
    89  var (
    90  	energyCallDataCopy   = memoryCopierEnergy(2)
    91  	energyCodeCopy       = memoryCopierEnergy(2)
    92  	energyExtCodeCopy    = memoryCopierEnergy(3)
    93  	energyReturnDataCopy = memoryCopierEnergy(2)
    94  )
    95  
    96  //  0. If *energyleft* is less than or equal to 2300, fail the current call.
    97  //  1. If current value equals new value (this is a no-op), SSTORE_NOOP_ENERGY energy is deducted.
    98  //  2. If current value does not equal new value:
    99  //     2.1. If original value equals current value (this storage slot has not been changed by the current execution context):
   100  //     2.1.1. If original value is 0, SSTORE_INIT_ENERGY energy is deducted.
   101  //     2.1.2. Otherwise, SSTORE_CLEAN_ENERGY energy is deducted. If new value is 0, add SSTORE_CLEAR_REFUND to refund counter.
   102  //     2.2. If original value does not equal current value (this storage slot is dirty), SSTORE_DIRTY_ENERGY energy is deducted. Apply both of the following clauses:
   103  //     2.2.1. If original value is not 0:
   104  //     2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEAR_REFUND energy from refund counter. We can prove that refund counter will never go below 0.
   105  //     2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEAR_REFUND energy to refund counter.
   106  //     2.2.2. If original value equals new value (this storage slot is reset):
   107  //     2.2.2.1. If original value is 0, add SSTORE_INIT_REFUND to refund counter.
   108  //     2.2.2.2. Otherwise, add SSTORE_CLEAN_REFUND energy to refund counter.
   109  func energySStore(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   110  	// If we fail the minimum energy availability invariant, fail (0)
   111  	if contract.Energy <= params.SstoreSentryEnergy {
   112  		return 0, errors.New("not enough energy for reentrancy sentry")
   113  	}
   114  	// Energy sentry honoured, do the actual energy calculation based on the stored value
   115  	var (
   116  		y, x    = stack.Back(1), stack.Back(0)
   117  		current = cvm.StateDB.GetState(contract.Address(), x.Bytes32())
   118  	)
   119  	value := common.Hash(y.Bytes32())
   120  
   121  	if current == value { // noop (1)
   122  		return params.SstoreNoopEnergy, nil
   123  	}
   124  	original := cvm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
   125  	if original == current {
   126  		if original == (common.Hash{}) { // create slot (2.1.1)
   127  			return params.SstoreInitEnergy, nil
   128  		}
   129  		if value == (common.Hash{}) { // delete slot (2.1.2b)
   130  			cvm.StateDB.AddRefund(params.SstoreClearRefund)
   131  		}
   132  		return params.SstoreCleanEnergy, nil // write existing slot (2.1.2)
   133  	}
   134  	if original != (common.Hash{}) {
   135  		if current == (common.Hash{}) { // recreate slot (2.2.1.1)
   136  			cvm.StateDB.SubRefund(params.SstoreClearRefund)
   137  		} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
   138  			cvm.StateDB.AddRefund(params.SstoreClearRefund)
   139  		}
   140  	}
   141  	if original == value {
   142  		if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
   143  			cvm.StateDB.AddRefund(params.SstoreInitRefund)
   144  		} else { // reset to original existing slot (2.2.2.2)
   145  			cvm.StateDB.AddRefund(params.SstoreCleanRefund)
   146  		}
   147  	}
   148  	return params.SstoreDirtyEnergy, nil // dirty update (2.2)
   149  }
   150  
   151  func makeEnergyLog(n uint64) energyFunc {
   152  	return func(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   153  		requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
   154  		if overflow {
   155  			return 0, ErrEnergyUintOverflow
   156  		}
   157  
   158  		energy, err := memoryEnergyCost(mem, memorySize)
   159  		if err != nil {
   160  			return 0, err
   161  		}
   162  
   163  		if energy, overflow = math.SafeAdd(energy, params.LogEnergy); overflow {
   164  			return 0, ErrEnergyUintOverflow
   165  		}
   166  		if energy, overflow = math.SafeAdd(energy, n*params.LogTopicEnergy); overflow {
   167  			return 0, ErrEnergyUintOverflow
   168  		}
   169  
   170  		var memorySizeEnergy uint64
   171  		if memorySizeEnergy, overflow = math.SafeMul(requestedSize, params.LogDataEnergy); overflow {
   172  			return 0, ErrEnergyUintOverflow
   173  		}
   174  		if energy, overflow = math.SafeAdd(energy, memorySizeEnergy); overflow {
   175  			return 0, ErrEnergyUintOverflow
   176  		}
   177  		return energy, nil
   178  	}
   179  }
   180  
   181  func energySha3(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   182  	energy, err := memoryEnergyCost(mem, memorySize)
   183  	if err != nil {
   184  		return 0, err
   185  	}
   186  	wordEnergy, overflow := stack.Back(1).Uint64WithOverflow()
   187  	if overflow {
   188  		return 0, ErrEnergyUintOverflow
   189  	}
   190  	if wordEnergy, overflow = math.SafeMul(toWordSize(wordEnergy), params.Sha3WordEnergy); overflow {
   191  		return 0, ErrEnergyUintOverflow
   192  	}
   193  	if energy, overflow = math.SafeAdd(energy, wordEnergy); overflow {
   194  		return 0, ErrEnergyUintOverflow
   195  	}
   196  	return energy, nil
   197  }
   198  
   199  // pureMemoryEnergycost is used by several operations, which aside from their
   200  // static cost have a dynamic cost which is solely based on the memory
   201  // expansion
   202  func pureMemoryEnergycost(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   203  	return memoryEnergyCost(mem, memorySize)
   204  }
   205  
   206  var (
   207  	energyReturn  = pureMemoryEnergycost
   208  	energyRevert  = pureMemoryEnergycost
   209  	energyMLoad   = pureMemoryEnergycost
   210  	energyMStore8 = pureMemoryEnergycost
   211  	energyMStore  = pureMemoryEnergycost
   212  	energyCreate  = pureMemoryEnergycost
   213  )
   214  
   215  func energyCreate2(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   216  	energy, err := memoryEnergyCost(mem, memorySize)
   217  	if err != nil {
   218  		return 0, err
   219  	}
   220  	wordEnergy, overflow := stack.Back(2).Uint64WithOverflow()
   221  	if overflow {
   222  		return 0, ErrEnergyUintOverflow
   223  	}
   224  	if wordEnergy, overflow = math.SafeMul(toWordSize(wordEnergy), params.Sha3WordEnergy); overflow {
   225  		return 0, ErrEnergyUintOverflow
   226  	}
   227  	if energy, overflow = math.SafeAdd(energy, wordEnergy); overflow {
   228  		return 0, ErrEnergyUintOverflow
   229  	}
   230  	return energy, nil
   231  }
   232  
   233  func energyExp(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   234  	expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
   235  
   236  	var (
   237  		energy   = expByteLen * params.ExpByte // no overflow check required. Max is 256 * ExpByte energy
   238  		overflow bool
   239  	)
   240  	if energy, overflow = math.SafeAdd(energy, params.ExpEnergy); overflow {
   241  		return 0, ErrEnergyUintOverflow
   242  	}
   243  	return energy, nil
   244  }
   245  
   246  func energyCall(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   247  	var (
   248  		energy         uint64
   249  		transfersValue = !stack.Back(2).IsZero()
   250  		address        = common.Address(stack.Back(1).Bytes22())
   251  	)
   252  	if transfersValue && cvm.StateDB.Empty(address) {
   253  		energy += params.CallNewAccountEnergy
   254  	}
   255  	if transfersValue {
   256  		energy += params.CallValueTransferEnergy
   257  	}
   258  	memoryEnergy, err := memoryEnergyCost(mem, memorySize)
   259  	if err != nil {
   260  		return 0, err
   261  	}
   262  	var overflow bool
   263  	if energy, overflow = math.SafeAdd(energy, memoryEnergy); overflow {
   264  		return 0, ErrEnergyUintOverflow
   265  	}
   266  
   267  	cvm.callEnergyTemp, err = callEnergy(contract.Energy, energy, stack.Back(0))
   268  	if err != nil {
   269  		return 0, err
   270  	}
   271  	if energy, overflow = math.SafeAdd(energy, cvm.callEnergyTemp); overflow {
   272  		return 0, ErrEnergyUintOverflow
   273  	}
   274  	return energy, nil
   275  }
   276  
   277  func energyCallCode(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   278  	memoryEnergy, err := memoryEnergyCost(mem, memorySize)
   279  	if err != nil {
   280  		return 0, err
   281  	}
   282  	var (
   283  		energy   uint64
   284  		overflow bool
   285  	)
   286  	if stack.Back(2).Sign() != 0 {
   287  		energy += params.CallValueTransferEnergy
   288  	}
   289  	if energy, overflow = math.SafeAdd(energy, memoryEnergy); overflow {
   290  		return 0, ErrEnergyUintOverflow
   291  	}
   292  	cvm.callEnergyTemp, err = callEnergy(contract.Energy, energy, stack.Back(0))
   293  	if err != nil {
   294  		return 0, err
   295  	}
   296  	if energy, overflow = math.SafeAdd(energy, cvm.callEnergyTemp); overflow {
   297  		return 0, ErrEnergyUintOverflow
   298  	}
   299  	return energy, nil
   300  }
   301  
   302  func energyDelegateCall(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   303  	energy, err := memoryEnergyCost(mem, memorySize)
   304  	if err != nil {
   305  		return 0, err
   306  	}
   307  	cvm.callEnergyTemp, err = callEnergy(contract.Energy, energy, stack.Back(0))
   308  	if err != nil {
   309  		return 0, err
   310  	}
   311  	var overflow bool
   312  	if energy, overflow = math.SafeAdd(energy, cvm.callEnergyTemp); overflow {
   313  		return 0, ErrEnergyUintOverflow
   314  	}
   315  	return energy, nil
   316  }
   317  
   318  func energyStaticCall(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   319  	energy, err := memoryEnergyCost(mem, memorySize)
   320  	if err != nil {
   321  		return 0, err
   322  	}
   323  	cvm.callEnergyTemp, err = callEnergy(contract.Energy, energy, stack.Back(0))
   324  	if err != nil {
   325  		return 0, err
   326  	}
   327  	var overflow bool
   328  	if energy, overflow = math.SafeAdd(energy, cvm.callEnergyTemp); overflow {
   329  		return 0, ErrEnergyUintOverflow
   330  	}
   331  	return energy, nil
   332  }
   333  
   334  func energySelfdestruct(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   335  	var energy uint64
   336  	energy = params.SelfdestructEnergy
   337  	var address = common.Address(stack.Back(0).Bytes22())
   338  
   339  	// if empty and transfers value
   340  	if cvm.StateDB.Empty(address) && cvm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
   341  		energy += params.CreateBySelfdestructEnergy
   342  	}
   343  
   344  	if !cvm.StateDB.HasSuicided(contract.Address()) {
   345  		cvm.StateDB.AddRefund(params.SelfdestructRefundEnergy)
   346  	}
   347  	return energy, nil
   348  }