mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 21:26:42 +00:00
core/vm: switch modexp gas computation to uint64
This commit is contained in:
parent
1263f3dfc1
commit
cc158997d3
1 changed files with 206 additions and 122 deletions
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"maps"
|
"maps"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"math/bits"
|
||||||
|
|
||||||
"github.com/consensys/gnark-crypto/ecc"
|
"github.com/consensys/gnark-crypto/ecc"
|
||||||
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
|
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
|
||||||
|
|
@ -38,6 +39,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
"github.com/ethereum/go-ethereum/crypto/secp256r1"
|
"github.com/ethereum/go-ethereum/crypto/secp256r1"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
"golang.org/x/crypto/ripemd160"
|
"golang.org/x/crypto/ripemd160"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -378,21 +380,21 @@ type bigModExp struct {
|
||||||
eip7883 bool
|
eip7883 bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// Constants for modexp gas calculation
|
||||||
big1 = big.NewInt(1)
|
const (
|
||||||
big3 = big.NewInt(3)
|
byzantiumMultiplier = 8
|
||||||
big7 = big.NewInt(7)
|
berlinMultiplier = 8
|
||||||
big20 = big.NewInt(20)
|
osakaMultiplier = 16
|
||||||
big32 = big.NewInt(32)
|
|
||||||
big64 = big.NewInt(64)
|
byzantiumDivisor = 20
|
||||||
big96 = big.NewInt(96)
|
berlinDivisor = 3
|
||||||
big480 = big.NewInt(480)
|
osakaDivisor = 3
|
||||||
big1024 = big.NewInt(1024)
|
|
||||||
big3072 = big.NewInt(3072)
|
berlinMinGas = 200
|
||||||
big199680 = big.NewInt(199680)
|
osakaMinGas = 500
|
||||||
)
|
)
|
||||||
|
|
||||||
// modexpMultComplexity implements bigModexp multComplexity formula, as defined in EIP-198
|
// byzantiumMultComplexity implements bigModexp multComplexity formula, as defined in EIP-198
|
||||||
//
|
//
|
||||||
// def mult_complexity(x):
|
// def mult_complexity(x):
|
||||||
// if x <= 64: return x ** 2
|
// if x <= 64: return x ** 2
|
||||||
|
|
@ -400,146 +402,228 @@ var (
|
||||||
// else: return x ** 2 // 16 + 480 * x - 199680
|
// else: return x ** 2 // 16 + 480 * x - 199680
|
||||||
//
|
//
|
||||||
// where is x is max(length_of_MODULUS, length_of_BASE)
|
// where is x is max(length_of_MODULUS, length_of_BASE)
|
||||||
func modexpMultComplexity(x *big.Int) *big.Int {
|
// returns true if an overflow occurred.
|
||||||
|
func byzantiumMultComplexity(x uint64) uint64 {
|
||||||
switch {
|
switch {
|
||||||
case x.Cmp(big64) <= 0:
|
case x <= 64:
|
||||||
x.Mul(x, x) // x ** 2
|
return x * x
|
||||||
case x.Cmp(big1024) <= 0:
|
case x <= 1024:
|
||||||
// (x ** 2 // 4 ) + ( 96 * x - 3072)
|
// x^2 / 4 + 96*x - 3072
|
||||||
x = new(big.Int).Add(
|
return x*x/4 + 96*x - 3072
|
||||||
new(big.Int).Rsh(new(big.Int).Mul(x, x), 2),
|
|
||||||
new(big.Int).Sub(new(big.Int).Mul(big96, x), big3072),
|
|
||||||
)
|
|
||||||
default:
|
default:
|
||||||
// (x ** 2 // 16) + (480 * x - 199680)
|
// For large x, use uint256 arithmetic to avoid overflow
|
||||||
x = new(big.Int).Add(
|
// x^2 / 16 + 480*x - 199680
|
||||||
new(big.Int).Rsh(new(big.Int).Mul(x, x), 4),
|
// xSqr = x^2
|
||||||
new(big.Int).Sub(new(big.Int).Mul(big480, x), big199680),
|
carry, xSqr := bits.Mul64(x, x)
|
||||||
)
|
if carry != 0 {
|
||||||
|
return math.MaxUint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate x^2 / 16 (right shift by 4 bits)
|
||||||
|
xSqr = xSqr >> 4
|
||||||
|
|
||||||
|
// Calculate 480 * x (can't overflow if x^2 didn't overflow)
|
||||||
|
x480 := x * 480
|
||||||
|
|
||||||
|
// Calculate 480 * x - 199680 (will not underflow, since x > 1024)
|
||||||
|
x480 = x480 - 199680
|
||||||
|
|
||||||
|
sum, carry := bits.Add64(xSqr, x480, 0)
|
||||||
|
if carry != 0 {
|
||||||
|
return math.MaxUint64
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// berlinMultComplexity implements the multiplication complexity formula for Berlin.
|
||||||
|
//
|
||||||
|
// def mult_complexity(x):
|
||||||
|
//
|
||||||
|
// ceiling(x/8)^2
|
||||||
|
//
|
||||||
|
// where is x is max(length_of_MODULUS, length_of_BASE)
|
||||||
|
func berlinMultComplexity(x uint64) uint64 {
|
||||||
|
// x = (x + 7) / 8
|
||||||
|
x, carry := bits.Add64(x, 7, 0)
|
||||||
|
if carry != 0 {
|
||||||
|
return math.MaxUint64
|
||||||
|
}
|
||||||
|
x /= 8
|
||||||
|
// x * x
|
||||||
|
carry, x = bits.Mul64(x, x)
|
||||||
|
if carry != 0 {
|
||||||
|
return math.MaxUint64
|
||||||
}
|
}
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Slow Bigint way (benchmark this)
|
||||||
|
// func berlinMultComplexity(xInt uint64) *big.Int {
|
||||||
|
// x := new(big.Int).SetUint64(xInt)
|
||||||
|
// x = new(big.Int).Add(x, big.NewInt(7)) // x + 7
|
||||||
|
// x = new(big.Int).Rsh(x, 3) // (x + 7) / 8
|
||||||
|
// return new(big.Int).Mul(x, x) // ((x + 7) / 8) ^ 2
|
||||||
|
// }
|
||||||
|
|
||||||
|
// osakaMultComplexity implements the multiplication complexity formula for Osaka.
|
||||||
|
//
|
||||||
|
// For x <= 32: returns 16
|
||||||
|
// For x > 32: returns 2 * ceiling(x/8)^2
|
||||||
|
func osakaMultComplexity(x uint64) uint64 {
|
||||||
|
if x <= 32 {
|
||||||
|
return 16
|
||||||
|
}
|
||||||
|
// For x > 32, return 2 * berlinMultComplexity(x)
|
||||||
|
result := berlinMultComplexity(x)
|
||||||
|
carry, result := bits.Mul64(result, 2)
|
||||||
|
if carry != 0 {
|
||||||
|
return math.MaxUint64
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateIterationCount calculates the number of iterations for the modexp precompile.
|
||||||
|
// This is the adjusted exponent length used in gas calculation.
|
||||||
|
func calculateIterationCount(expLen uint64, expHead uint256.Int, multiplier uint64) uint64 {
|
||||||
|
var iterationCount uint64
|
||||||
|
|
||||||
|
// For large exponents (expLen > 32), add (expLen - 32) * multiplier
|
||||||
|
if expLen > 32 {
|
||||||
|
iterationCount = (expLen - 32) * multiplier
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the MSB position - 1 if expHead is non-zero
|
||||||
|
if bitLen := expHead.BitLen(); bitLen > 0 {
|
||||||
|
iterationCount += uint64(bitLen - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return max(iterationCount, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// byzantiumGasCalc calculates the gas cost for the modexp precompile using Byzantium rules.
|
||||||
|
func byzantiumGasCalc(baseLen, expLen, modLen uint64, expHead uint256.Int) uint64 {
|
||||||
|
maxLen := max(baseLen, modLen)
|
||||||
|
multComplexity := byzantiumMultComplexity(maxLen)
|
||||||
|
iterationCount := calculateIterationCount(expLen, expHead, byzantiumMultiplier)
|
||||||
|
|
||||||
|
// Calculate gas: (multComplexity * iterationCount) / byzantiumDivisor
|
||||||
|
carry, gas := bits.Mul64(iterationCount, multComplexity)
|
||||||
|
gas /= byzantiumDivisor
|
||||||
|
if carry != 0 {
|
||||||
|
return math.MaxUint64
|
||||||
|
}
|
||||||
|
return gas
|
||||||
|
}
|
||||||
|
|
||||||
|
// berlinGasCalc calculates the gas cost for the modexp precompile using Berlin rules.
|
||||||
|
func berlinGasCalc(baseLen, expLen, modLen uint64, expHead uint256.Int) uint64 {
|
||||||
|
maxLen := max(baseLen, modLen)
|
||||||
|
multComplexity := berlinMultComplexity(maxLen)
|
||||||
|
iterationCount := calculateIterationCount(expLen, expHead, berlinMultiplier)
|
||||||
|
|
||||||
|
// Calculate gas: (multComplexity * iterationCount) / berlinDivisor
|
||||||
|
carry, gas := bits.Mul64(iterationCount, multComplexity)
|
||||||
|
gas /= berlinDivisor
|
||||||
|
if carry != 0 {
|
||||||
|
return math.MaxUint64
|
||||||
|
}
|
||||||
|
|
||||||
|
return max(gas, berlinMinGas)
|
||||||
|
}
|
||||||
|
|
||||||
|
// osakaGasCalc calculates the gas cost for the modexp precompile using Osaka rules.
|
||||||
|
func osakaGasCalc(baseLen, expLen, modLen uint64, expHead uint256.Int) uint64 {
|
||||||
|
maxLen := max(baseLen, modLen)
|
||||||
|
multComplexity := osakaMultComplexity(maxLen)
|
||||||
|
iterationCount := calculateIterationCount(expLen, expHead, osakaMultiplier)
|
||||||
|
|
||||||
|
// Calculate gas: (multComplexity * iterationCount) / osakaDivisor
|
||||||
|
carry, gas := bits.Mul64(iterationCount, multComplexity)
|
||||||
|
gas /= osakaDivisor
|
||||||
|
if carry != 0 {
|
||||||
|
return math.MaxUint64
|
||||||
|
}
|
||||||
|
|
||||||
|
return max(gas, osakaMinGas)
|
||||||
|
}
|
||||||
|
|
||||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||||
func (c *bigModExp) RequiredGas(input []byte) uint64 {
|
func (c *bigModExp) RequiredGas(input []byte) uint64 {
|
||||||
var (
|
// Parse input lengths
|
||||||
baseLen = new(big.Int).SetBytes(getData(input, 0, 32))
|
baseLenBig := new(uint256.Int).SetBytes(getData(input, 0, 32))
|
||||||
expLen = new(big.Int).SetBytes(getData(input, 32, 32))
|
expLenBig := new(uint256.Int).SetBytes(getData(input, 32, 32))
|
||||||
modLen = new(big.Int).SetBytes(getData(input, 64, 32))
|
modLenBig := new(uint256.Int).SetBytes(getData(input, 64, 32))
|
||||||
)
|
|
||||||
|
// Convert to uint64, capping at max value
|
||||||
|
baseLen := baseLenBig.Uint64()
|
||||||
|
if !baseLenBig.IsUint64() {
|
||||||
|
baseLen = math.MaxUint64
|
||||||
|
}
|
||||||
|
expLen := expLenBig.Uint64()
|
||||||
|
if !expLenBig.IsUint64() {
|
||||||
|
expLen = math.MaxUint64
|
||||||
|
}
|
||||||
|
modLen := modLenBig.Uint64()
|
||||||
|
if !modLenBig.IsUint64() {
|
||||||
|
modLen = math.MaxUint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the header
|
||||||
if len(input) > 96 {
|
if len(input) > 96 {
|
||||||
input = input[96:]
|
input = input[96:]
|
||||||
} else {
|
} else {
|
||||||
input = input[:0]
|
input = input[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the head 32 bytes of exp for the adjusted exponent length
|
// Retrieve the head 32 bytes of exp for the adjusted exponent length
|
||||||
var expHead *big.Int
|
var expHead uint256.Int
|
||||||
if big.NewInt(int64(len(input))).Cmp(baseLen) <= 0 {
|
if uint64(len(input)) > baseLen {
|
||||||
expHead = new(big.Int)
|
if expLen > 32 {
|
||||||
|
expHead.SetBytes(getData(input, baseLen, 32))
|
||||||
} else {
|
} else {
|
||||||
if expLen.Cmp(big32) > 0 {
|
// TODO: Check that if expLen < baseLen, then getData will return an empty slice
|
||||||
expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), 32))
|
expHead.SetBytes(getData(input, baseLen, expLen))
|
||||||
} else {
|
|
||||||
expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), expLen.Uint64()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Calculate the adjusted exponent length
|
|
||||||
var msb int
|
// Choose the appropriate gas calculation based on the EIP flags
|
||||||
if bitlen := expHead.BitLen(); bitlen > 0 {
|
|
||||||
msb = bitlen - 1
|
|
||||||
}
|
|
||||||
adjExpLen := new(big.Int)
|
|
||||||
if expLen.Cmp(big32) > 0 {
|
|
||||||
adjExpLen.Sub(expLen, big32)
|
|
||||||
if c.eip7883 {
|
if c.eip7883 {
|
||||||
adjExpLen.Lsh(adjExpLen, 4)
|
return osakaGasCalc(baseLen, expLen, modLen, expHead)
|
||||||
|
} else if c.eip2565 {
|
||||||
|
return berlinGasCalc(baseLen, expLen, modLen, expHead)
|
||||||
} else {
|
} else {
|
||||||
adjExpLen.Lsh(adjExpLen, 3)
|
return byzantiumGasCalc(baseLen, expLen, modLen, expHead)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
adjExpLen.Add(adjExpLen, big.NewInt(int64(msb)))
|
|
||||||
// Calculate the gas cost of the operation
|
|
||||||
gas := new(big.Int)
|
|
||||||
if modLen.Cmp(baseLen) < 0 {
|
|
||||||
gas.Set(baseLen)
|
|
||||||
} else {
|
|
||||||
gas.Set(modLen)
|
|
||||||
}
|
|
||||||
|
|
||||||
maxLenOver32 := gas.Cmp(big32) > 0
|
|
||||||
if c.eip2565 {
|
|
||||||
// EIP-2565 (Berlin fork) has three changes:
|
|
||||||
//
|
|
||||||
// 1. Different multComplexity (inlined here)
|
|
||||||
// in EIP-2565 (https://eips.ethereum.org/EIPS/eip-2565):
|
|
||||||
//
|
|
||||||
// def mult_complexity(x):
|
|
||||||
// ceiling(x/8)^2
|
|
||||||
//
|
|
||||||
// where is x is max(length_of_MODULUS, length_of_BASE)
|
|
||||||
gas.Add(gas, big7)
|
|
||||||
gas.Rsh(gas, 3)
|
|
||||||
gas.Mul(gas, gas)
|
|
||||||
|
|
||||||
var minPrice uint64 = 200
|
|
||||||
if c.eip7883 {
|
|
||||||
minPrice = 500
|
|
||||||
if maxLenOver32 {
|
|
||||||
gas.Add(gas, gas)
|
|
||||||
} else {
|
|
||||||
gas = big.NewInt(16)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if adjExpLen.Cmp(big1) > 0 {
|
|
||||||
gas.Mul(gas, adjExpLen)
|
|
||||||
}
|
|
||||||
// 2. Different divisor (`GQUADDIVISOR`) (3)
|
|
||||||
if !c.eip7883 {
|
|
||||||
gas.Div(gas, big3)
|
|
||||||
}
|
|
||||||
if gas.BitLen() > 64 {
|
|
||||||
return math.MaxUint64
|
|
||||||
}
|
|
||||||
return max(minPrice, gas.Uint64())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-Berlin logic.
|
|
||||||
gas = modexpMultComplexity(gas)
|
|
||||||
if adjExpLen.Cmp(big1) > 0 {
|
|
||||||
gas.Mul(gas, adjExpLen)
|
|
||||||
}
|
|
||||||
gas.Div(gas, big20)
|
|
||||||
if gas.BitLen() > 64 {
|
|
||||||
return math.MaxUint64
|
|
||||||
}
|
|
||||||
return gas.Uint64()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *bigModExp) Run(input []byte) ([]byte, error) {
|
func (c *bigModExp) Run(input []byte) ([]byte, error) {
|
||||||
var (
|
// Extract lengths - they're 32-byte big-endian integers
|
||||||
baseLenBig = new(big.Int).SetBytes(getData(input, 0, 32))
|
// with the actual uint64 value in the last 8 bytes
|
||||||
expLenBig = new(big.Int).SetBytes(getData(input, 32, 32))
|
var baseLen, expLen, modLen uint64
|
||||||
modLenBig = new(big.Int).SetBytes(getData(input, 64, 32))
|
if len(input) >= 32 {
|
||||||
baseLen = baseLenBig.Uint64()
|
baseLen = binary.BigEndian.Uint64(input[24:32])
|
||||||
expLen = expLenBig.Uint64()
|
}
|
||||||
modLen = modLenBig.Uint64()
|
if len(input) >= 64 {
|
||||||
inputLenOverflow = max(baseLenBig.BitLen(), expLenBig.BitLen(), modLenBig.BitLen()) > 64
|
expLen = binary.BigEndian.Uint64(input[56:64])
|
||||||
)
|
}
|
||||||
|
if len(input) >= 96 {
|
||||||
|
modLen = binary.BigEndian.Uint64(input[88:96])
|
||||||
|
}
|
||||||
|
|
||||||
if len(input) > 96 {
|
if len(input) > 96 {
|
||||||
input = input[96:]
|
input = input[96:]
|
||||||
} else {
|
} else {
|
||||||
input = input[:0]
|
input = input[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// enforce size cap for inputs
|
|
||||||
if c.eip7823 && (inputLenOverflow || max(baseLen, expLen, modLen) > 1024) {
|
|
||||||
return nil, errors.New("one or more of base/exponent/modulus length exceeded 1024 bytes")
|
|
||||||
}
|
|
||||||
// Handle a special case when both the base and mod length is zero
|
// Handle a special case when both the base and mod length is zero
|
||||||
if baseLen == 0 && modLen == 0 {
|
if baseLen == 0 && modLen == 0 {
|
||||||
return []byte{}, nil
|
return []byte{}, nil
|
||||||
}
|
}
|
||||||
|
// enforce size cap for inputs
|
||||||
|
if c.eip7823 && max(baseLen, expLen, modLen) > 1024 {
|
||||||
|
return nil, fmt.Errorf("one or more of base/exponent/modulus length exceeded 1024 bytes")
|
||||||
|
}
|
||||||
// Retrieve the operands and execute the exponentiation
|
// Retrieve the operands and execute the exponentiation
|
||||||
var (
|
var (
|
||||||
base = new(big.Int).SetBytes(getData(input, 0, baseLen))
|
base = new(big.Int).SetBytes(getData(input, 0, baseLen))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue