From 02495e6c3b6cf6f09459181cb8abb0be9f087d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Kunze=20K=C3=BCllmer?= <31522760+fedekunze@users.noreply.github.com> Date: Tue, 17 Jan 2023 08:48:13 +0100 Subject: [PATCH 01/11] feat(vm): stateful precompiles --- core/vm/contract.go | 49 +++++++++++++++++++++-- core/vm/contracts.go | 82 ++++++++++++++++++++++++++------------- core/vm/contracts_test.go | 2 +- core/vm/evm.go | 8 ++-- 4 files changed, 105 insertions(+), 36 deletions(-) diff --git a/core/vm/contract.go b/core/vm/contract.go index bb0902969ec7..9595f21d0516 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -58,12 +58,13 @@ type Contract struct { CodeAddr *common.Address Input []byte - Gas uint64 - value *big.Int + Gas uint64 + value *big.Int + isPrecompile bool } // NewContract returns a new contract environment for the execution of EVM. -func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract { +func NewContract(caller, object ContractRef, value *big.Int, gas uint64) *Contract { c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object} if parent, ok := caller.(*Contract); ok { @@ -82,7 +83,34 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uin return c } +// NewPrecompile returns a new instance of a precompiled contract environment for the execution of EVM. +func NewPrecompile(caller, object ContractRef, value *big.Int, gas uint64) *Contract { + c := &Contract{ + CallerAddress: caller.Address(), + caller: caller, + self: object, + isPrecompile: true, + } + + // Gas should be a pointer so it can safely be reduced through the run + // This pointer will be off the state transition + c.Gas = gas + // ensures a value is set + c.value = value + + return c +} + +// IsPrecompile returns true if the contract is a precompiled contract environment +func (c Contract) IsPrecompile() bool { + return c.isPrecompile +} + func (c *Contract) validJumpdest(dest *uint256.Int) bool { + if c.isPrecompile { + return false + } + udest, overflow := dest.Uint64WithOverflow() // PC cannot go beyond len(code) and certainly can't be bigger than 63bits. // Don't bother checking for JUMPDEST in that case. @@ -99,6 +127,10 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool { // isCode returns true if the provided PC location is an actual opcode, as // opposed to a data-segment following a PUSHN operation. func (c *Contract) isCode(udest uint64) bool { + if c.isPrecompile { + return false + } + // Do we already have an analysis laying around? if c.analysis != nil { return c.analysis.codeSegment(udest) @@ -132,6 +164,9 @@ func (c *Contract) isCode(udest uint64) bool { // AsDelegate sets the contract to be a delegate call and returns the current // contract (for chaining calls) func (c *Contract) AsDelegate() *Contract { + if c.isPrecompile { + return c + } // NOTE: caller must, at all times be a contract. It should never happen // that caller is something other than a Contract. parent := c.caller.(*Contract) @@ -180,6 +215,10 @@ func (c *Contract) Value() *big.Int { // SetCallCode sets the code of the contract and address of the backing data // object func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) { + if c.isPrecompile { + return + } + c.Code = code c.CodeHash = hash c.CodeAddr = addr @@ -188,6 +227,10 @@ func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []by // SetCodeOptionalHash can be used to provide code, but it's optional to provide hash. // In case hash is not provided, the jumpdest analysis will not be saved to the parent context func (c *Contract) SetCodeOptionalHash(addr *common.Address, codeAndHash *codeAndHash) { + if c.isPrecompile { + return + } + c.Code = codeAndHash.code c.CodeHash = codeAndHash.hash c.CodeAddr = addr diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 225a9e6a978c..e40fa2b9a2f2 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -40,8 +40,10 @@ import ( // contract. type PrecompiledContract interface { ContractRef - RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use - Run(input []byte) ([]byte, error) // Run runs the precompiled contract + // RequiredPrice calculates the contract gas used + RequiredGas(input []byte) uint64 + // Run runs the precompiled contract + Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) } // PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum @@ -257,14 +259,38 @@ func ValidatePrecompiles( // - the returned bytes, // - the _remaining_ gas, // - any error that occurred -func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) { +func (evm *EVM) RunPrecompiledContract( + p PrecompiledContract, + caller ContractRef, + addr common.Address, + input []byte, + suppliedGas uint64, + value *big.Int, + readonly bool, +) (ret []byte, remainingGas uint64, err error) { + return runPrecompiledContract(evm, p, caller, addr, input, suppliedGas, value, readonly) +} + +func runPrecompiledContract( + evm *EVM, + p PrecompiledContract, + caller ContractRef, + addr common.Address, + input []byte, + suppliedGas uint64, + value *big.Int, + readonly bool, +) (ret []byte, remainingGas uint64, err error) { + addrCopy := addr + contract := NewPrecompile(caller, AccountRef(addrCopy), value, suppliedGas) + gasCost := p.RequiredGas(input) if suppliedGas < gasCost { return nil, 0, ErrOutOfGas } - suppliedGas -= gasCost - output, err := p.Run(input) - return output, suppliedGas, err + + output, err := p.Run(evm, contract, input, readonly) + return output, contract.Gas, err } // ECRECOVER implemented as a native contract. @@ -280,7 +306,7 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 { return params.EcrecoverGas } -func (c *ecrecover) Run(input []byte) ([]byte, error) { +func (c *ecrecover) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { const ecRecoverInputLength = 128 input = common.RightPadBytes(input, ecRecoverInputLength) @@ -328,7 +354,7 @@ func (c *sha256hash) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas } -func (c *sha256hash) Run(input []byte) ([]byte, error) { +func (c *sha256hash) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { h := sha256.Sum256(input) return h[:], nil } @@ -350,7 +376,7 @@ func (c *ripemd160hash) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas } -func (c *ripemd160hash) Run(input []byte) ([]byte, error) { +func (c *ripemd160hash) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { ripemd := ripemd160.New() ripemd.Write(input) return common.LeftPadBytes(ripemd.Sum(nil), 32), nil @@ -373,8 +399,8 @@ func (c *dataCopy) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas } -func (c *dataCopy) Run(in []byte) ([]byte, error) { - return common.CopyBytes(in), nil +func (c *dataCopy) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { + return common.CopyBytes(input), nil } // bigModExp implements a native big integer exponential modular operation. @@ -505,7 +531,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { return gas.Uint64() } -func (c *bigModExp) Run(input []byte) ([]byte, error) { +func (c *bigModExp) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { var ( baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64() expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64() @@ -591,7 +617,7 @@ func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256AddGasIstanbul } -func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) { +func (c *bn256AddIstanbul) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { return runBn256Add(input) } @@ -610,7 +636,7 @@ func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 { return params.Bn256AddGasByzantium } -func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) { +func (c *bn256AddByzantium) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { return runBn256Add(input) } @@ -641,7 +667,7 @@ func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasIstanbul } -func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) { +func (c *bn256ScalarMulIstanbul) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { return runBn256ScalarMul(input) } @@ -660,7 +686,7 @@ func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasByzantium } -func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) { +func (c *bn256ScalarMulByzantium) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { return runBn256ScalarMul(input) } @@ -721,7 +747,7 @@ func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul } -func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) { +func (c *bn256PairingIstanbul) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { return runBn256Pairing(input) } @@ -740,7 +766,7 @@ func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium } -func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) { +func (c *bn256PairingByzantium) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { return runBn256Pairing(input) } @@ -772,7 +798,7 @@ var ( errBlake2FInvalidFinalFlag = errors.New("invalid final flag") ) -func (c *blake2F) Run(input []byte) ([]byte, error) { +func (c *blake2F) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { // Make sure the input is valid (correct length and final flag) if len(input) != blake2FInputLength { return nil, errBlake2FInvalidInputLength @@ -832,7 +858,7 @@ func (c *bls12381G1Add) RequiredGas(input []byte) uint64 { return params.Bls12381G1AddGas } -func (c *bls12381G1Add) Run(input []byte) ([]byte, error) { +func (c *bls12381G1Add) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { // Implements EIP-2537 G1Add precompile. // > G1 addition call expects `256` bytes as an input that is interpreted as byte concatenation of two G1 points (`128` bytes each). // > Output is an encoding of addition operation result - single G1 point (`128` bytes). @@ -876,7 +902,7 @@ func (c *bls12381G1Mul) RequiredGas(input []byte) uint64 { return params.Bls12381G1MulGas } -func (c *bls12381G1Mul) Run(input []byte) ([]byte, error) { +func (c *bls12381G1Mul) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { // Implements EIP-2537 G1Mul precompile. // > G1 multiplication call expects `160` bytes as an input that is interpreted as byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes). // > Output is an encoding of multiplication operation result - single G1 point (`128` bytes). @@ -932,7 +958,7 @@ func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 { return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000 } -func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) { +func (c *bls12381G1MultiExp) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { // Implements EIP-2537 G1MultiExp precompile. // G1 multiplication call expects `160*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes). // Output is an encoding of multiexponentiation operation result - single G1 point (`128` bytes). @@ -981,7 +1007,7 @@ func (c *bls12381G2Add) RequiredGas(input []byte) uint64 { return params.Bls12381G2AddGas } -func (c *bls12381G2Add) Run(input []byte) ([]byte, error) { +func (c *bls12381G2Add) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { // Implements EIP-2537 G2Add precompile. // > G2 addition call expects `512` bytes as an input that is interpreted as byte concatenation of two G2 points (`256` bytes each). // > Output is an encoding of addition operation result - single G2 point (`256` bytes). @@ -1025,7 +1051,7 @@ func (c *bls12381G2Mul) RequiredGas(input []byte) uint64 { return params.Bls12381G2MulGas } -func (c *bls12381G2Mul) Run(input []byte) ([]byte, error) { +func (c *bls12381G2Mul) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { // Implements EIP-2537 G2MUL precompile logic. // > G2 multiplication call expects `288` bytes as an input that is interpreted as byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes). // > Output is an encoding of multiplication operation result - single G2 point (`256` bytes). @@ -1081,7 +1107,7 @@ func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 { return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000 } -func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) { +func (c *bls12381G2MultiExp) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { // Implements EIP-2537 G2MultiExp precompile logic // > G2 multiplication call expects `288*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes). // > Output is an encoding of multiexponentiation operation result - single G2 point (`256` bytes). @@ -1130,7 +1156,7 @@ func (c *bls12381Pairing) RequiredGas(input []byte) uint64 { return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas } -func (c *bls12381Pairing) Run(input []byte) ([]byte, error) { +func (c *bls12381Pairing) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { // Implements EIP-2537 Pairing precompile logic. // > Pairing call expects `384*k` bytes as an inputs that is interpreted as byte concatenation of `k` slices. Each slice has the following structure: // > - `128` bytes of G1 point encoding @@ -1215,7 +1241,7 @@ func (c *bls12381MapG1) RequiredGas(input []byte) uint64 { return params.Bls12381MapG1Gas } -func (c *bls12381MapG1) Run(input []byte) ([]byte, error) { +func (c *bls12381MapG1) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { // Implements EIP-2537 Map_To_G1 precompile. // > Field-to-curve call expects `64` bytes an an input that is interpreted as a an element of the base field. // > Output of this call is `128` bytes and is G1 point following respective encoding rules. @@ -1256,7 +1282,7 @@ func (c *bls12381MapG2) RequiredGas(input []byte) uint64 { return params.Bls12381MapG2Gas } -func (c *bls12381MapG2) Run(input []byte) ([]byte, error) { +func (c *bls12381MapG2) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { // Implements EIP-2537 Map_FP2_TO_G2 precompile logic. // > Field-to-curve call expects `128` bytes an an input that is interpreted as a an element of the quadratic extension field. // > Output of this call is `256` bytes and is G2 point following respective encoding rules. diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index b22d999e6cd9..770fc1e9095e 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -179,7 +179,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { // Keep it as uint64, multiply 100 to get two digit float later mgasps := (100 * 1000 * gasUsed) / elapsed bench.ReportMetric(float64(mgasps)/100, "mgas/s") - //Check if it is correct + // Check if it is correct if err != nil { bench.Error(err) return diff --git a/core/vm/evm.go b/core/vm/evm.go index 0a26647f83df..bc12573bc689 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -209,7 +209,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } if isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = evm.RunPrecompiledContract(p, caller, addr, input, gas, value, false) } else { // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. @@ -272,7 +272,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.Precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = evm.RunPrecompiledContract(p, caller, addr, input, gas, value, false) } else { addrCopy := addr // Initialise a new contract and set the code that is to be used by the EVM. @@ -313,7 +313,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.Precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = evm.RunPrecompiledContract(p, caller, addr, input, gas, nil, true) } else { addrCopy := addr // Initialise a new contract and make initialise the delegate values @@ -362,7 +362,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte } if p, isPrecompile := evm.Precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = evm.RunPrecompiledContract(p, caller, addr, input, gas, new(big.Int), false) } else { // At this point, we use a copy of address. If we don't, the go compiler will // leak the 'contract' to the outer scope, and make allocation for 'contract' From 1d71e770c23ddeb34ed94645b783a3a623484891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Kunze=20K=C3=BCllmer?= <31522760+fedekunze@users.noreply.github.com> Date: Tue, 17 Jan 2023 16:17:29 +0100 Subject: [PATCH 02/11] IsStateful func --- core/vm/contract.go | 1 + core/vm/contracts.go | 66 +++++++++++++++++++++++++++++++++++++++++++ core/vm/jump_table.go | 2 +- 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/core/vm/contract.go b/core/vm/contract.go index 9595f21d0516..cf820ce274a7 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -25,6 +25,7 @@ import ( // ContractRef is a reference to the contract's backing object type ContractRef interface { + // Address returns the contract's address Address() common.Address } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index e40fa2b9a2f2..1f1d49d5e158 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -40,6 +40,9 @@ import ( // contract. type PrecompiledContract interface { ContractRef + // IsStateful returns true if the precompile contract executes a state + // transition. + IsStateful() bool // RequiredPrice calculates the contract gas used RequiredGas(input []byte) uint64 // Run runs the precompiled contract @@ -302,6 +305,9 @@ func (ecrecover) Address() common.Address { return common.BytesToAddress([]byte{1}) } +// IsStateful returns false. +func (ecrecover) IsStateful() bool { return false } + func (c *ecrecover) RequiredGas(input []byte) uint64 { return params.EcrecoverGas } @@ -346,6 +352,9 @@ func (sha256hash) Address() common.Address { return common.BytesToAddress([]byte{2}) } +// IsStateful returns false. +func (sha256hash) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. // // This method does not require any overflow checking as the input size gas costs @@ -368,6 +377,9 @@ func (ripemd160hash) Address() common.Address { return common.BytesToAddress([]byte{3}) } +// IsStateful returns false. +func (ripemd160hash) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. // // This method does not require any overflow checking as the input size gas costs @@ -391,6 +403,9 @@ func (dataCopy) Address() common.Address { return common.BytesToAddress([]byte{4}) } +// IsStateful returns false. +func (dataCopy) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. // // This method does not require any overflow checking as the input size gas costs @@ -460,6 +475,9 @@ func (bigModExp) Address() common.Address { return common.BytesToAddress([]byte{5}) } +// IsStateful returns false. +func (bigModExp) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bigModExp) RequiredGas(input []byte) uint64 { var ( @@ -612,6 +630,9 @@ func (bn256AddIstanbul) Address() common.Address { return common.BytesToAddress([]byte{6}) } +// IsStateful returns false. +func (bn256AddIstanbul) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256AddGasIstanbul @@ -631,6 +652,9 @@ func (bn256AddByzantium) Address() common.Address { return common.BytesToAddress([]byte{6}) } +// IsStateful returns false. +func (bn256AddByzantium) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 { return params.Bn256AddGasByzantium @@ -662,6 +686,9 @@ func (bn256ScalarMulIstanbul) Address() common.Address { return common.BytesToAddress([]byte{7}) } +// IsStateful returns false. +func (bn256ScalarMulIstanbul) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasIstanbul @@ -681,6 +708,9 @@ func (bn256ScalarMulByzantium) Address() common.Address { return common.BytesToAddress([]byte{7}) } +// IsStateful returns false. +func (bn256ScalarMulByzantium) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasByzantium @@ -742,6 +772,9 @@ func (bn256PairingIstanbul) Address() common.Address { return common.BytesToAddress([]byte{8}) } +// IsStateful returns false. +func (bn256PairingIstanbul) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul @@ -761,6 +794,9 @@ func (bn256PairingByzantium) Address() common.Address { return common.BytesToAddress([]byte{8}) } +// IsStateful returns false. +func (bn256PairingByzantium) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium @@ -778,6 +814,9 @@ func (blake2F) Address() common.Address { return common.BytesToAddress([]byte{9}) } +// IsStateful returns false. +func (blake2F) IsStateful() bool { return false } + func (c *blake2F) RequiredGas(input []byte) uint64 { // If the input is malformed, we can't calculate the gas, return 0 and let the // actual call choke and fault. @@ -853,6 +892,9 @@ func (bls12381G1Add) Address() common.Address { return common.BytesToAddress([]byte{10}) } +// IsStateful returns false. +func (bls12381G1Add) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381G1Add) RequiredGas(input []byte) uint64 { return params.Bls12381G1AddGas @@ -897,6 +939,9 @@ func (bls12381G1Mul) Address() common.Address { return common.BytesToAddress([]byte{11}) } +// IsStateful returns false. +func (bls12381G1Mul) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381G1Mul) RequiredGas(input []byte) uint64 { return params.Bls12381G1MulGas @@ -939,6 +984,9 @@ func (bls12381G1MultiExp) Address() common.Address { return common.BytesToAddress([]byte{12}) } +// IsStateful returns false. +func (bls12381G1MultiExp) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 { // Calculate G1 point, scalar value pair length @@ -1002,6 +1050,9 @@ func (bls12381G2Add) Address() common.Address { return common.BytesToAddress([]byte{13}) } +// IsStateful returns false. +func (bls12381G2Add) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381G2Add) RequiredGas(input []byte) uint64 { return params.Bls12381G2AddGas @@ -1046,6 +1097,9 @@ func (bls12381G2Mul) Address() common.Address { return common.BytesToAddress([]byte{14}) } +// IsStateful returns false. +func (bls12381G2Mul) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381G2Mul) RequiredGas(input []byte) uint64 { return params.Bls12381G2MulGas @@ -1088,6 +1142,9 @@ func (bls12381G2MultiExp) Address() common.Address { return common.BytesToAddress([]byte{15}) } +// IsStateful returns false. +func (bls12381G2MultiExp) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 { // Calculate G2 point, scalar value pair length @@ -1151,6 +1208,9 @@ func (bls12381Pairing) Address() common.Address { return common.BytesToAddress([]byte{16}) } +// IsStateful returns false. +func (bls12381Pairing) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381Pairing) RequiredGas(input []byte) uint64 { return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas @@ -1236,6 +1296,9 @@ func (bls12381MapG1) Address() common.Address { return common.BytesToAddress([]byte{17}) } +// IsStateful returns false. +func (bls12381MapG1) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381MapG1) RequiredGas(input []byte) uint64 { return params.Bls12381MapG1Gas @@ -1277,6 +1340,9 @@ func (bls12381MapG2) Address() common.Address { return common.BytesToAddress([]byte{18}) } +// IsStateful returns false. +func (bls12381MapG2) IsStateful() bool { return false } + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381MapG2) RequiredGas(input []byte) uint64 { return params.Bls12381MapG2Gas diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index f6813589dcb2..37ece3a331b5 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -24,7 +24,7 @@ import ( type ( executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) - gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64 + gasFunc func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) // memorySizeFunc returns the required size, and whether the operation overflowed a uint64 memorySizeFunc func(*Stack) (size uint64, overflow bool) ) From b4fe3baef09f2a827691ee1a084e05e76d0fd4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Kunze=20K=C3=BCllmer?= <31522760+fedekunze@users.noreply.github.com> Date: Tue, 17 Jan 2023 16:43:04 +0100 Subject: [PATCH 03/11] readOnly --- core/vm/contracts.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 1f1d49d5e158..74d3a70a44c5 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -269,9 +269,9 @@ func (evm *EVM) RunPrecompiledContract( input []byte, suppliedGas uint64, value *big.Int, - readonly bool, + readOnly bool, ) (ret []byte, remainingGas uint64, err error) { - return runPrecompiledContract(evm, p, caller, addr, input, suppliedGas, value, readonly) + return runPrecompiledContract(evm, p, caller, addr, input, suppliedGas, value, readOnly) } func runPrecompiledContract( @@ -282,7 +282,7 @@ func runPrecompiledContract( input []byte, suppliedGas uint64, value *big.Int, - readonly bool, + readOnly bool, ) (ret []byte, remainingGas uint64, err error) { addrCopy := addr contract := NewPrecompile(caller, AccountRef(addrCopy), value, suppliedGas) @@ -292,7 +292,7 @@ func runPrecompiledContract( return nil, 0, ErrOutOfGas } - output, err := p.Run(evm, contract, input, readonly) + output, err := p.Run(evm, contract, input, readOnly) return output, contract.Gas, err } From abb98709fecb8047b81eddc4da032f228a034277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Kunze=20K=C3=BCllmer?= <31522760+fedekunze@users.noreply.github.com> Date: Tue, 17 Jan 2023 16:53:12 +0100 Subject: [PATCH 04/11] improvements --- core/vm/contracts.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 74d3a70a44c5..a4a1cd730d66 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -40,8 +40,8 @@ import ( // contract. type PrecompiledContract interface { ContractRef - // IsStateful returns true if the precompile contract executes a state - // transition. + // IsStateful returns true if the precompile contract can execute a state + // transition or if it can access the StateDB. IsStateful() bool // RequiredPrice calculates the contract gas used RequiredGas(input []byte) uint64 From a366152b7435aa636ab3ce3c373d98e9d49ef29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Kunze=20K=C3=BCllmer?= <31522760+fedekunze@users.noreply.github.com> Date: Mon, 30 Jan 2023 12:35:25 +0100 Subject: [PATCH 05/11] fix tests --- core/vm/contracts_test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index 770fc1e9095e..f2d1e80e7878 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/json" "fmt" + "math/big" "os" "testing" "time" @@ -96,7 +97,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { - if res, _, err := RunPrecompiledContract(p, in, gas); err != nil { + if res, _, err := runPrecompiledContract(nil, p, AccountRef(common.Address{}), p.Address(), in, gas, new(big.Int), false); err != nil { t.Error(err) } else if common.Bytes2Hex(res) != test.Expected { t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)) @@ -118,7 +119,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { gas := p.RequiredGas(in) - 1 t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { - _, _, err := RunPrecompiledContract(p, in, gas) + _, _, err := runPrecompiledContract(nil, p, AccountRef(common.Address{}), p.Address(), in, gas, new(big.Int), false) if err.Error() != "out of gas" { t.Errorf("Expected error [out of gas], got [%v]", err) } @@ -135,7 +136,7 @@ func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(test.Name, func(t *testing.T) { - _, _, err := RunPrecompiledContract(p, in, gas) + _, _, err := runPrecompiledContract(nil, p, AccountRef(common.Address{}), p.Address(), in, gas, new(big.Int), false) if err.Error() != test.ExpectedError { t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err) } @@ -167,7 +168,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { copy(data, in) - res, _, err = RunPrecompiledContract(p, data, reqGas) + res, _, err = runPrecompiledContract(nil, p, AccountRef(common.Address{}), p.Address(), in, reqGas, new(big.Int), false) } bench.StopTimer() elapsed := uint64(time.Since(start)) From 6ab8a955cd0199f0621ac2b542da31a01d0f8796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Kunze=20K=C3=BCllmer?= <31522760+fedekunze@users.noreply.github.com> Date: Mon, 30 Jan 2023 12:47:16 +0100 Subject: [PATCH 06/11] fix contract input --- core/vm/contracts.go | 4 ++++ tests/fuzzers/bls12381/precompile_fuzzer.go | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index a4a1cd730d66..c221da20d9ea 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -285,7 +285,11 @@ func runPrecompiledContract( readOnly bool, ) (ret []byte, remainingGas uint64, err error) { addrCopy := addr + inputCopy := make([]byte, len(input)) + copy(inputCopy, input) + contract := NewPrecompile(caller, AccountRef(addrCopy), value, suppliedGas) + contract.Input = inputCopy gasCost := p.RequiredGas(input) if suppliedGas < gasCost { diff --git a/tests/fuzzers/bls12381/precompile_fuzzer.go b/tests/fuzzers/bls12381/precompile_fuzzer.go index cab2bcba3863..cff366a05255 100644 --- a/tests/fuzzers/bls12381/precompile_fuzzer.go +++ b/tests/fuzzers/bls12381/precompile_fuzzer.go @@ -92,7 +92,8 @@ func fuzz(id byte, data []byte) int { } cpy := make([]byte, len(data)) copy(cpy, data) - _, err := precompile.Run(cpy) + contract := vm.NewPrecompile(vm.AccountRef(common.Address{}), precompile, common.Big0, gas) + _, err := precompile.Run(nil, contract, cpy, true) if !bytes.Equal(cpy, data) { panic(fmt.Sprintf("input data modified, precompile %d: %x %x", id, data, cpy)) } From 4c36f274d58cff16a301b207c28d80ddd84c9582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Kunze=20K=C3=BCllmer?= <31522760+fedekunze@users.noreply.github.com> Date: Mon, 30 Jan 2023 12:58:45 +0100 Subject: [PATCH 07/11] imp address --- core/vm/contracts.go | 6 ++---- core/vm/contracts_test.go | 8 ++++---- core/vm/evm.go | 9 +++++---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index c221da20d9ea..99676e7db402 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -265,26 +265,24 @@ func ValidatePrecompiles( func (evm *EVM) RunPrecompiledContract( p PrecompiledContract, caller ContractRef, - addr common.Address, input []byte, suppliedGas uint64, value *big.Int, readOnly bool, ) (ret []byte, remainingGas uint64, err error) { - return runPrecompiledContract(evm, p, caller, addr, input, suppliedGas, value, readOnly) + return runPrecompiledContract(evm, p, caller, input, suppliedGas, value, readOnly) } func runPrecompiledContract( evm *EVM, p PrecompiledContract, caller ContractRef, - addr common.Address, input []byte, suppliedGas uint64, value *big.Int, readOnly bool, ) (ret []byte, remainingGas uint64, err error) { - addrCopy := addr + addrCopy := p.Address() inputCopy := make([]byte, len(input)) copy(inputCopy, input) diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index f2d1e80e7878..1d4417c5b9af 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -97,7 +97,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { - if res, _, err := runPrecompiledContract(nil, p, AccountRef(common.Address{}), p.Address(), in, gas, new(big.Int), false); err != nil { + if res, _, err := runPrecompiledContract(nil, p, AccountRef(common.Address{}), in, gas, new(big.Int), false); err != nil { t.Error(err) } else if common.Bytes2Hex(res) != test.Expected { t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)) @@ -119,7 +119,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { gas := p.RequiredGas(in) - 1 t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { - _, _, err := runPrecompiledContract(nil, p, AccountRef(common.Address{}), p.Address(), in, gas, new(big.Int), false) + _, _, err := runPrecompiledContract(nil, p, AccountRef(common.Address{}), in, gas, new(big.Int), false) if err.Error() != "out of gas" { t.Errorf("Expected error [out of gas], got [%v]", err) } @@ -136,7 +136,7 @@ func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(test.Name, func(t *testing.T) { - _, _, err := runPrecompiledContract(nil, p, AccountRef(common.Address{}), p.Address(), in, gas, new(big.Int), false) + _, _, err := runPrecompiledContract(nil, p, AccountRef(common.Address{}), in, gas, new(big.Int), false) if err.Error() != test.ExpectedError { t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err) } @@ -168,7 +168,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { copy(data, in) - res, _, err = runPrecompiledContract(nil, p, AccountRef(common.Address{}), p.Address(), in, reqGas, new(big.Int), false) + res, _, err = runPrecompiledContract(nil, p, AccountRef(common.Address{}), in, reqGas, new(big.Int), false) } bench.StopTimer() elapsed := uint64(time.Since(start)) diff --git a/core/vm/evm.go b/core/vm/evm.go index bc12573bc689..431b9b2cc7ee 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -209,7 +209,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } if isPrecompile { - ret, gas, err = evm.RunPrecompiledContract(p, caller, addr, input, gas, value, false) + ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, value, false) } else { // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. @@ -272,7 +272,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.Precompile(addr); isPrecompile { - ret, gas, err = evm.RunPrecompiledContract(p, caller, addr, input, gas, value, false) + ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, value, false) } else { addrCopy := addr // Initialise a new contract and set the code that is to be used by the EVM. @@ -313,7 +313,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.Precompile(addr); isPrecompile { - ret, gas, err = evm.RunPrecompiledContract(p, caller, addr, input, gas, nil, true) + ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, nil, true) } else { addrCopy := addr // Initialise a new contract and make initialise the delegate values @@ -362,7 +362,8 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte } if p, isPrecompile := evm.Precompile(addr); isPrecompile { - ret, gas, err = evm.RunPrecompiledContract(p, caller, addr, input, gas, new(big.Int), false) + // Note: delegate call is not allowed to modify state on precompiles + ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, new(big.Int), true) } else { // At this point, we use a copy of address. If we don't, the go compiler will // leak the 'contract' to the outer scope, and make allocation for 'contract' From 10ff4e5aea2bfee3663e7207e84416b1627818e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Kunze=20K=C3=BCllmer?= <31522760+fedekunze@users.noreply.github.com> Date: Mon, 30 Jan 2023 13:03:50 +0100 Subject: [PATCH 08/11] rm input from args --- core/vm/contracts.go | 170 +++++++++++++++++++++---------------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 99676e7db402..368abb575631 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -46,7 +46,7 @@ type PrecompiledContract interface { // RequiredPrice calculates the contract gas used RequiredGas(input []byte) uint64 // Run runs the precompiled contract - Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) + Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) } // PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum @@ -294,7 +294,7 @@ func runPrecompiledContract( return nil, 0, ErrOutOfGas } - output, err := p.Run(evm, contract, input, readOnly) + output, err := p.Run(evm, contract, readOnly) return output, contract.Gas, err } @@ -314,28 +314,28 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 { return params.EcrecoverGas } -func (c *ecrecover) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { +func (c *ecrecover) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { const ecRecoverInputLength = 128 - input = common.RightPadBytes(input, ecRecoverInputLength) + contract.Input = common.RightPadBytes(contract.Input, ecRecoverInputLength) // "input" is (hash, v, r, s), each 32 bytes // but for ecrecover we want (r, s, v) - r := new(big.Int).SetBytes(input[64:96]) - s := new(big.Int).SetBytes(input[96:128]) - v := input[63] - 27 + r := new(big.Int).SetBytes(contract.Input[64:96]) + s := new(big.Int).SetBytes(contract.Input[96:128]) + v := contract.Input[63] - 27 // tighter sig s values input homestead only apply to tx sigs - if !allZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) { + if !allZero(contract.Input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) { return nil, nil } // We must make sure not to modify the 'input', so placing the 'v' along with // the signature needs to be done on a new allocation sig := make([]byte, 65) - copy(sig, input[64:128]) + copy(sig, contract.Input[64:128]) sig[64] = v // v needs to be at the end for libsecp256k1 - pubKey, err := crypto.Ecrecover(input[:32], sig) + pubKey, err := crypto.Ecrecover(contract.Input[:32], sig) // make sure the public key is a valid one if err != nil { return nil, nil @@ -365,8 +365,8 @@ func (c *sha256hash) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas } -func (c *sha256hash) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { - h := sha256.Sum256(input) +func (c *sha256hash) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + h := sha256.Sum256(contract.Input) return h[:], nil } @@ -390,9 +390,9 @@ func (c *ripemd160hash) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas } -func (c *ripemd160hash) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { +func (c *ripemd160hash) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { ripemd := ripemd160.New() - ripemd.Write(input) + ripemd.Write(contract.Input) return common.LeftPadBytes(ripemd.Sum(nil), 32), nil } @@ -416,8 +416,8 @@ func (c *dataCopy) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas } -func (c *dataCopy) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { - return common.CopyBytes(input), nil +func (c *dataCopy) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + return common.CopyBytes(contract.Input), nil } // bigModExp implements a native big integer exponential modular operation. @@ -551,16 +551,16 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { return gas.Uint64() } -func (c *bigModExp) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { +func (c *bigModExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { var ( - baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64() - expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64() - modLen = new(big.Int).SetBytes(getData(input, 64, 32)).Uint64() + baseLen = new(big.Int).SetBytes(getData(contract.Input, 0, 32)).Uint64() + expLen = new(big.Int).SetBytes(getData(contract.Input, 32, 32)).Uint64() + modLen = new(big.Int).SetBytes(getData(contract.Input, 64, 32)).Uint64() ) - if len(input) > 96 { - input = input[96:] + if len(contract.Input) > 96 { + contract.Input = contract.Input[96:] } else { - input = input[:0] + contract.Input = contract.Input[:0] } // Handle a special case when both the base and mod length is zero if baseLen == 0 && modLen == 0 { @@ -568,9 +568,9 @@ func (c *bigModExp) Run(evm *EVM, contract *Contract, input []byte, readonly boo } // Retrieve the operands and execute the exponentiation var ( - base = new(big2.Int).SetBytes(getData(input, 0, baseLen)) - exp = new(big2.Int).SetBytes(getData(input, baseLen, expLen)) - mod = new(big2.Int).SetBytes(getData(input, baseLen+expLen, modLen)) + base = new(big2.Int).SetBytes(getData(contract.Input, 0, baseLen)) + exp = new(big2.Int).SetBytes(getData(contract.Input, baseLen, expLen)) + mod = new(big2.Int).SetBytes(getData(contract.Input, baseLen+expLen, modLen)) v []byte ) switch { @@ -640,8 +640,8 @@ func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256AddGasIstanbul } -func (c *bn256AddIstanbul) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { - return runBn256Add(input) +func (c *bn256AddIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + return runBn256Add(contract.Input) } // bn256AddByzantium implements a native elliptic curve point addition @@ -662,8 +662,8 @@ func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 { return params.Bn256AddGasByzantium } -func (c *bn256AddByzantium) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { - return runBn256Add(input) +func (c *bn256AddByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + return runBn256Add(contract.Input) } // runBn256ScalarMul implements the Bn256ScalarMul precompile, referenced by @@ -696,8 +696,8 @@ func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasIstanbul } -func (c *bn256ScalarMulIstanbul) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { - return runBn256ScalarMul(input) +func (c *bn256ScalarMulIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + return runBn256ScalarMul(contract.Input) } // bn256ScalarMulByzantium implements a native elliptic curve scalar @@ -718,8 +718,8 @@ func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasByzantium } -func (c *bn256ScalarMulByzantium) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { - return runBn256ScalarMul(input) +func (c *bn256ScalarMulByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + return runBn256ScalarMul(contract.Input) } var ( @@ -782,8 +782,8 @@ func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul } -func (c *bn256PairingIstanbul) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { - return runBn256Pairing(input) +func (c *bn256PairingIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + return runBn256Pairing(contract.Input) } // bn256PairingByzantium implements a pairing pre-compile for the bn256 curve @@ -804,8 +804,8 @@ func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium } -func (c *bn256PairingByzantium) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { - return runBn256Pairing(input) +func (c *bn256PairingByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + return runBn256Pairing(contract.Input) } type blake2F struct{} @@ -839,18 +839,18 @@ var ( errBlake2FInvalidFinalFlag = errors.New("invalid final flag") ) -func (c *blake2F) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { +func (c *blake2F) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { // Make sure the input is valid (correct length and final flag) - if len(input) != blake2FInputLength { + if len(contract.Input) != blake2FInputLength { return nil, errBlake2FInvalidInputLength } - if input[212] != blake2FNonFinalBlockBytes && input[212] != blake2FFinalBlockBytes { + if contract.Input[212] != blake2FNonFinalBlockBytes && contract.Input[212] != blake2FFinalBlockBytes { return nil, errBlake2FInvalidFinalFlag } // Parse the input into the Blake2b call parameters var ( - rounds = binary.BigEndian.Uint32(input[0:4]) - final = input[212] == blake2FFinalBlockBytes + rounds = binary.BigEndian.Uint32(contract.Input[0:4]) + final = contract.Input[212] == blake2FFinalBlockBytes h [8]uint64 m [16]uint64 @@ -858,14 +858,14 @@ func (c *blake2F) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ) for i := 0; i < 8; i++ { offset := 4 + i*8 - h[i] = binary.LittleEndian.Uint64(input[offset : offset+8]) + h[i] = binary.LittleEndian.Uint64(contract.Input[offset : offset+8]) } for i := 0; i < 16; i++ { offset := 68 + i*8 - m[i] = binary.LittleEndian.Uint64(input[offset : offset+8]) + m[i] = binary.LittleEndian.Uint64(contract.Input[offset : offset+8]) } - t[0] = binary.LittleEndian.Uint64(input[196:204]) - t[1] = binary.LittleEndian.Uint64(input[204:212]) + t[0] = binary.LittleEndian.Uint64(contract.Input[196:204]) + t[1] = binary.LittleEndian.Uint64(contract.Input[204:212]) // Execute the compression function, extract and return the result blake2b.F(&h, m, t, final, rounds) @@ -902,11 +902,11 @@ func (c *bls12381G1Add) RequiredGas(input []byte) uint64 { return params.Bls12381G1AddGas } -func (c *bls12381G1Add) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { +func (c *bls12381G1Add) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { // Implements EIP-2537 G1Add precompile. // > G1 addition call expects `256` bytes as an input that is interpreted as byte concatenation of two G1 points (`128` bytes each). // > Output is an encoding of addition operation result - single G1 point (`128` bytes). - if len(input) != 256 { + if len(contract.Input) != 256 { return nil, errBLS12381InvalidInputLength } var err error @@ -916,11 +916,11 @@ func (c *bls12381G1Add) Run(evm *EVM, contract *Contract, input []byte, readonly g := bls12381.NewG1() // Decode G1 point p_0 - if p0, err = g.DecodePoint(input[:128]); err != nil { + if p0, err = g.DecodePoint(contract.Input[:128]); err != nil { return nil, err } // Decode G1 point p_1 - if p1, err = g.DecodePoint(input[128:]); err != nil { + if p1, err = g.DecodePoint(contract.Input[128:]); err != nil { return nil, err } @@ -949,11 +949,11 @@ func (c *bls12381G1Mul) RequiredGas(input []byte) uint64 { return params.Bls12381G1MulGas } -func (c *bls12381G1Mul) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { +func (c *bls12381G1Mul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { // Implements EIP-2537 G1Mul precompile. // > G1 multiplication call expects `160` bytes as an input that is interpreted as byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes). // > Output is an encoding of multiplication operation result - single G1 point (`128` bytes). - if len(input) != 160 { + if len(contract.Input) != 160 { return nil, errBLS12381InvalidInputLength } var err error @@ -963,11 +963,11 @@ func (c *bls12381G1Mul) Run(evm *EVM, contract *Contract, input []byte, readonly g := bls12381.NewG1() // Decode G1 point - if p0, err = g.DecodePoint(input[:128]); err != nil { + if p0, err = g.DecodePoint(contract.Input[:128]); err != nil { return nil, err } // Decode scalar value - e := new(big.Int).SetBytes(input[128:]) + e := new(big.Int).SetBytes(contract.Input[128:]) // Compute r = e * p_0 r := g.New() @@ -1008,12 +1008,12 @@ func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 { return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000 } -func (c *bls12381G1MultiExp) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { +func (c *bls12381G1MultiExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { // Implements EIP-2537 G1MultiExp precompile. // G1 multiplication call expects `160*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes). // Output is an encoding of multiexponentiation operation result - single G1 point (`128` bytes). - k := len(input) / 160 - if len(input) == 0 || len(input)%160 != 0 { + k := len(contract.Input) / 160 + if len(contract.Input) == 0 || len(contract.Input)%160 != 0 { return nil, errBLS12381InvalidInputLength } var err error @@ -1028,11 +1028,11 @@ func (c *bls12381G1MultiExp) Run(evm *EVM, contract *Contract, input []byte, rea off := 160 * i t0, t1, t2 := off, off+128, off+160 // Decode G1 point - if points[i], err = g.DecodePoint(input[t0:t1]); err != nil { + if points[i], err = g.DecodePoint(contract.Input[t0:t1]); err != nil { return nil, err } // Decode scalar value - scalars[i] = new(big.Int).SetBytes(input[t1:t2]) + scalars[i] = new(big.Int).SetBytes(contract.Input[t1:t2]) } // Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1) @@ -1060,11 +1060,11 @@ func (c *bls12381G2Add) RequiredGas(input []byte) uint64 { return params.Bls12381G2AddGas } -func (c *bls12381G2Add) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { +func (c *bls12381G2Add) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { // Implements EIP-2537 G2Add precompile. // > G2 addition call expects `512` bytes as an input that is interpreted as byte concatenation of two G2 points (`256` bytes each). // > Output is an encoding of addition operation result - single G2 point (`256` bytes). - if len(input) != 512 { + if len(contract.Input) != 512 { return nil, errBLS12381InvalidInputLength } var err error @@ -1075,11 +1075,11 @@ func (c *bls12381G2Add) Run(evm *EVM, contract *Contract, input []byte, readonly r := g.New() // Decode G2 point p_0 - if p0, err = g.DecodePoint(input[:256]); err != nil { + if p0, err = g.DecodePoint(contract.Input[:256]); err != nil { return nil, err } // Decode G2 point p_1 - if p1, err = g.DecodePoint(input[256:]); err != nil { + if p1, err = g.DecodePoint(contract.Input[256:]); err != nil { return nil, err } @@ -1107,11 +1107,11 @@ func (c *bls12381G2Mul) RequiredGas(input []byte) uint64 { return params.Bls12381G2MulGas } -func (c *bls12381G2Mul) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { +func (c *bls12381G2Mul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { // Implements EIP-2537 G2MUL precompile logic. // > G2 multiplication call expects `288` bytes as an input that is interpreted as byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes). // > Output is an encoding of multiplication operation result - single G2 point (`256` bytes). - if len(input) != 288 { + if len(contract.Input) != 288 { return nil, errBLS12381InvalidInputLength } var err error @@ -1121,11 +1121,11 @@ func (c *bls12381G2Mul) Run(evm *EVM, contract *Contract, input []byte, readonly g := bls12381.NewG2() // Decode G2 point - if p0, err = g.DecodePoint(input[:256]); err != nil { + if p0, err = g.DecodePoint(contract.Input[:256]); err != nil { return nil, err } // Decode scalar value - e := new(big.Int).SetBytes(input[256:]) + e := new(big.Int).SetBytes(contract.Input[256:]) // Compute r = e * p_0 r := g.New() @@ -1166,12 +1166,12 @@ func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 { return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000 } -func (c *bls12381G2MultiExp) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { +func (c *bls12381G2MultiExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { // Implements EIP-2537 G2MultiExp precompile logic // > G2 multiplication call expects `288*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes). // > Output is an encoding of multiexponentiation operation result - single G2 point (`256` bytes). - k := len(input) / 288 - if len(input) == 0 || len(input)%288 != 0 { + k := len(contract.Input) / 288 + if len(contract.Input) == 0 || len(contract.Input)%288 != 0 { return nil, errBLS12381InvalidInputLength } var err error @@ -1186,11 +1186,11 @@ func (c *bls12381G2MultiExp) Run(evm *EVM, contract *Contract, input []byte, rea off := 288 * i t0, t1, t2 := off, off+256, off+288 // Decode G1 point - if points[i], err = g.DecodePoint(input[t0:t1]); err != nil { + if points[i], err = g.DecodePoint(contract.Input[t0:t1]); err != nil { return nil, err } // Decode scalar value - scalars[i] = new(big.Int).SetBytes(input[t1:t2]) + scalars[i] = new(big.Int).SetBytes(contract.Input[t1:t2]) } // Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1) @@ -1218,15 +1218,15 @@ func (c *bls12381Pairing) RequiredGas(input []byte) uint64 { return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas } -func (c *bls12381Pairing) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { +func (c *bls12381Pairing) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { // Implements EIP-2537 Pairing precompile logic. // > Pairing call expects `384*k` bytes as an inputs that is interpreted as byte concatenation of `k` slices. Each slice has the following structure: // > - `128` bytes of G1 point encoding // > - `256` bytes of G2 point encoding // > Output is a `32` bytes where last single byte is `0x01` if pairing result is equal to multiplicative identity in a pairing target field and `0x00` otherwise // > (which is equivalent of Big Endian encoding of Solidity values `uint256(1)` and `uin256(0)` respectively). - k := len(input) / 384 - if len(input) == 0 || len(input)%384 != 0 { + k := len(contract.Input) / 384 + if len(contract.Input) == 0 || len(contract.Input)%384 != 0 { return nil, errBLS12381InvalidInputLength } @@ -1240,12 +1240,12 @@ func (c *bls12381Pairing) Run(evm *EVM, contract *Contract, input []byte, readon t0, t1, t2 := off, off+128, off+384 // Decode G1 point - p1, err := g1.DecodePoint(input[t0:t1]) + p1, err := g1.DecodePoint(contract.Input[t0:t1]) if err != nil { return nil, err } // Decode G2 point - p2, err := g2.DecodePoint(input[t1:t2]) + p2, err := g2.DecodePoint(contract.Input[t1:t2]) if err != nil { return nil, err } @@ -1306,16 +1306,16 @@ func (c *bls12381MapG1) RequiredGas(input []byte) uint64 { return params.Bls12381MapG1Gas } -func (c *bls12381MapG1) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { +func (c *bls12381MapG1) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { // Implements EIP-2537 Map_To_G1 precompile. // > Field-to-curve call expects `64` bytes an an input that is interpreted as a an element of the base field. // > Output of this call is `128` bytes and is G1 point following respective encoding rules. - if len(input) != 64 { + if len(contract.Input) != 64 { return nil, errBLS12381InvalidInputLength } // Decode input field element - fe, err := decodeBLS12381FieldElement(input) + fe, err := decodeBLS12381FieldElement(contract.Input) if err != nil { return nil, err } @@ -1350,22 +1350,22 @@ func (c *bls12381MapG2) RequiredGas(input []byte) uint64 { return params.Bls12381MapG2Gas } -func (c *bls12381MapG2) Run(evm *EVM, contract *Contract, input []byte, readonly bool) ([]byte, error) { +func (c *bls12381MapG2) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { // Implements EIP-2537 Map_FP2_TO_G2 precompile logic. // > Field-to-curve call expects `128` bytes an an input that is interpreted as a an element of the quadratic extension field. // > Output of this call is `256` bytes and is G2 point following respective encoding rules. - if len(input) != 128 { + if len(contract.Input) != 128 { return nil, errBLS12381InvalidInputLength } // Decode input field element fe := make([]byte, 96) - c0, err := decodeBLS12381FieldElement(input[:64]) + c0, err := decodeBLS12381FieldElement(contract.Input[:64]) if err != nil { return nil, err } copy(fe[48:], c0) - c1, err := decodeBLS12381FieldElement(input[64:]) + c1, err := decodeBLS12381FieldElement(contract.Input[64:]) if err != nil { return nil, err } From c1ba1e27cfdb65c24ad706cbd23247bacd796148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Kunze=20K=C3=BCllmer?= <31522760+fedekunze@users.noreply.github.com> Date: Mon, 30 Jan 2023 13:55:38 +0100 Subject: [PATCH 09/11] fix test --- tests/fuzzers/bls12381/precompile_fuzzer.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/fuzzers/bls12381/precompile_fuzzer.go b/tests/fuzzers/bls12381/precompile_fuzzer.go index cff366a05255..537c72984c53 100644 --- a/tests/fuzzers/bls12381/precompile_fuzzer.go +++ b/tests/fuzzers/bls12381/precompile_fuzzer.go @@ -93,7 +93,8 @@ func fuzz(id byte, data []byte) int { cpy := make([]byte, len(data)) copy(cpy, data) contract := vm.NewPrecompile(vm.AccountRef(common.Address{}), precompile, common.Big0, gas) - _, err := precompile.Run(nil, contract, cpy, true) + contract.Input = cpy + _, err := precompile.Run(nil, contract, false) if !bytes.Equal(cpy, data) { panic(fmt.Sprintf("input data modified, precompile %d: %x %x", id, data, cpy)) } From d205b36ecc3b8b92bb56d12b3250b3eee1cb9960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Kunze=20K=C3=BCllmer?= <31522760+fedekunze@users.noreply.github.com> Date: Mon, 30 Jan 2023 14:01:06 +0100 Subject: [PATCH 10/11] c++ --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f21495876a9..3eb2bebb86f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## Unreleased +### State Machie Breaking + +* [#10](https://github.com/evmos/go-ethereum/pull/10) Support stateful precompiled contracts. + ### Improvements * [#8](https://github.com/evmos/go-ethereum/pull/8) Add `Address` function to `PrecompiledContract` interface. From 076bb67add9f7def7d5e88995363dedf1c932a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Kunze=20K=C3=BCllmer?= <31522760+fedekunze@users.noreply.github.com> Date: Mon, 30 Jan 2023 18:01:34 +0100 Subject: [PATCH 11/11] fix gas consumption --- core/vm/contracts.go | 4 ++-- eth/tracers/internal/tracetest/calltrace_test.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 368abb575631..02bffb4d347a 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -290,8 +290,8 @@ func runPrecompiledContract( contract.Input = inputCopy gasCost := p.RequiredGas(input) - if suppliedGas < gasCost { - return nil, 0, ErrOutOfGas + if !contract.UseGas(gasCost) { + return nil, contract.Gas, ErrOutOfGas } output, err := p.Run(evm, contract, readOnly) diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index 5cfb5b33c1b0..4d482342fd0e 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -264,7 +264,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { // Tx to A, A calls B with zero value. B does not already exist. // Expected: that enter/exit is invoked and the inner call is shown in the result func TestZeroValueToNotExitCall(t *testing.T) { - var to = common.HexToAddress("0x00000000000000000000000000000000deadbeef") + to := common.HexToAddress("0x00000000000000000000000000000000deadbeef") privkey, err := crypto.HexToECDSA("0000000000000000deadbeef00000000000000000000000000000000deadbeef") if err != nil { t.Fatalf("err %v", err) @@ -292,12 +292,12 @@ func TestZeroValueToNotExitCall(t *testing.T) { Difficulty: big.NewInt(0x30000), GasLimit: uint64(6000000), } - var code = []byte{ + code := []byte{ byte(vm.PUSH1), 0x0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), // in and outs zero byte(vm.DUP1), byte(vm.PUSH1), 0xff, byte(vm.GAS), // value=0,address=0xff, gas=GAS byte(vm.CALL), } - var alloc = core.GenesisAlloc{ + alloc := core.GenesisAlloc{ to: core.GenesisAccount{ Nonce: 1, Code: code,