Created
September 3, 2024 03:56
gas burn precompile prototype
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/core/vm/contracts.go b/core/vm/contracts.go | |
index 6f894790f..2c533bfdd 100644 | |
--- a/core/vm/contracts.go | |
+++ b/core/vm/contracts.go | |
@@ -36,6 +36,7 @@ import ( | |
"github.com/ethereum/go-ethereum/crypto/kzg4844" | |
"github.com/ethereum/go-ethereum/crypto/secp256r1" | |
"github.com/ethereum/go-ethereum/params" | |
+ "github.com/holiman/uint256" | |
"golang.org/x/crypto/ripemd160" | |
) | |
@@ -43,8 +44,8 @@ import ( | |
// requires a deterministic gas count based on the input size of the Run method of the | |
// contract. | |
type PrecompiledContract interface { | |
- RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use | |
- Run(input []byte) ([]byte, error) // Run runs the precompiled contract | |
+ RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use | |
+ Run(input []byte, stateDB StateDB) ([]byte, error) // Run runs the precompiled contract | |
} | |
// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum | |
@@ -237,7 +238,7 @@ func ActivePrecompiles(rules params.Rules) []common.Address { | |
// - the returned bytes, | |
// - the _remaining_ gas, | |
// - any error that occurred | |
-func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { | |
+func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, stateDB StateDB, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { | |
gasCost := p.RequiredGas(input) | |
if suppliedGas < gasCost { | |
return nil, 0, ErrOutOfGas | |
@@ -246,10 +247,69 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uin | |
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract) | |
} | |
suppliedGas -= gasCost | |
- output, err := p.Run(input) | |
+ output, err := p.Run(input, stateDB) | |
return output, suppliedGas, err | |
} | |
+type gasburn struct{} | |
+ | |
+// Burn all gas passed into precompile | |
+func (c *gasburn) RequiredGas(input []byte) uint64 { | |
+ gas := new(big.Int).SetBytes(input[64:96]) | |
+ return gas.Uint64() | |
+} | |
+ | |
+var systemAddress = common.HexToAddress("0x4334") | |
+ | |
+/** | |
+contract System { | |
+ fallback() { | |
+ bytes memory data = abi.encode(address(this), msg.sender, msg.value, gasleft(), block.basefee) | |
+ address(0x1e).call{ value: msg.value }(data) | |
+ } | |
+} | |
+*/ | |
+ | |
+/** | |
+Only the above system contract can call this precompile. | |
+We know it will always pass in the correct evm context via calldata, | |
+so we can manipulate the StateDB directly in a secure way from within | |
+the precompile. | |
+ | |
+Any gas that is passed to the precompile is burned in the `RequiredGas` | |
+function. We want to burn the gas based on fair market price and | |
+mint ether to the caller. | |
+*/ | |
+ | |
+// calldata | |
+// 0x00:0x20 system address | |
+// 0x00:0x40 msg.sender of system address | |
+// 0x40:0x60 msg.value <- maybe we don't need this? | |
+// 0x60:0x80 gasleft | |
+// 0x80:0x100 basefee | |
+func (c *gasburn) Run(input []byte, db StateDB) ([]byte, error) { | |
+ caller := common.Address(input[0:32]) | |
+ if caller != systemAddress { | |
+ return nil, errors.New("only system contract can call this") | |
+ } | |
+ // msg.sender in system address is forwarded to precompile | |
+ sender := common.Address(input[32:64]) | |
+ // amount of value passed in by call | |
+ //msgValue := new(big.Int).SetBytes(input[64:96]) | |
+ | |
+ // gas passed in call by system address | |
+ gasLeft := new(big.Int).SetBytes(input[96:128]) | |
+ // system address forwards the basefee | |
+ basefee := new(big.Int).SetBytes(input[128:160]) | |
+ // multiply the gas left and the basefee | |
+ value := new(big.Int).Mul(gasLeft, basefee) | |
+ // todo: be less lazy with types | |
+ val, _ := uint256.FromBig(value) | |
+ db.AddBalance(sender, val, tracing.BalanceChangeUnspecified) | |
+ | |
+ return nil, nil | |
+} | |
+ | |
// ecrecover implemented as a native contract. | |
type ecrecover struct{} | |
@@ -257,7 +317,7 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 { | |
return params.EcrecoverGas | |
} | |
-func (c *ecrecover) Run(input []byte) ([]byte, error) { | |
+func (c *ecrecover) Run(input []byte, _ StateDB) ([]byte, error) { | |
const ecRecoverInputLength = 128 | |
input = common.RightPadBytes(input, ecRecoverInputLength) | |
@@ -298,7 +358,7 @@ type sha256hash struct{} | |
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(input []byte, _ StateDB) ([]byte, error) { | |
h := sha256.Sum256(input) | |
return h[:], nil | |
} | |
@@ -313,7 +373,7 @@ type ripemd160hash struct{} | |
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(input []byte, _ StateDB) ([]byte, error) { | |
ripemd := ripemd160.New() | |
ripemd.Write(input) | |
return common.LeftPadBytes(ripemd.Sum(nil), 32), nil | |
@@ -329,7 +389,7 @@ type dataCopy struct{} | |
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) { | |
+func (c *dataCopy) Run(in []byte, _ StateDB) ([]byte, error) { | |
return common.CopyBytes(in), nil | |
} | |
@@ -451,7 +511,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { | |
return gas.Uint64() | |
} | |
-func (c *bigModExp) Run(input []byte) ([]byte, error) { | |
+func (c *bigModExp) Run(input []byte, _ StateDB) ([]byte, error) { | |
var ( | |
baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64() | |
expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64() | |
@@ -531,7 +591,7 @@ func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 { | |
return params.Bn256AddGasIstanbul | |
} | |
-func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) { | |
+func (c *bn256AddIstanbul) Run(input []byte, _ StateDB) ([]byte, error) { | |
return runBn256Add(input) | |
} | |
@@ -544,7 +604,7 @@ func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 { | |
return params.Bn256AddGasByzantium | |
} | |
-func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) { | |
+func (c *bn256AddByzantium) Run(input []byte, _ StateDB) ([]byte, error) { | |
return runBn256Add(input) | |
} | |
@@ -569,7 +629,7 @@ func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 { | |
return params.Bn256ScalarMulGasIstanbul | |
} | |
-func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) { | |
+func (c *bn256ScalarMulIstanbul) Run(input []byte, _ StateDB) ([]byte, error) { | |
return runBn256ScalarMul(input) | |
} | |
@@ -582,7 +642,7 @@ func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 { | |
return params.Bn256ScalarMulGasByzantium | |
} | |
-func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) { | |
+func (c *bn256ScalarMulByzantium) Run(input []byte, _ StateDB) ([]byte, error) { | |
return runBn256ScalarMul(input) | |
} | |
@@ -640,7 +700,7 @@ func (c *bn256PairingGranite) RequiredGas(input []byte) uint64 { | |
return new(bn256PairingIstanbul).RequiredGas(input) | |
} | |
-func (c *bn256PairingGranite) Run(input []byte) ([]byte, error) { | |
+func (c *bn256PairingGranite) Run(input []byte, _ StateDB) ([]byte, error) { | |
if len(input) > int(params.Bn256PairingMaxInputSizeGranite) { | |
return nil, errBadPairingInputSize | |
} | |
@@ -656,7 +716,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(input []byte, _ StateDB) ([]byte, error) { | |
return runBn256Pairing(input) | |
} | |
@@ -669,7 +729,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(input []byte, _ StateDB) ([]byte, error) { | |
return runBn256Pairing(input) | |
} | |
@@ -695,7 +755,7 @@ var ( | |
errBlake2FInvalidFinalFlag = errors.New("invalid final flag") | |
) | |
-func (c *blake2F) Run(input []byte) ([]byte, error) { | |
+func (c *blake2F) Run(input []byte, _ StateDB) ([]byte, error) { | |
// Make sure the input is valid (correct length and final flag) | |
if len(input) != blake2FInputLength { | |
return nil, errBlake2FInvalidInputLength | |
@@ -749,7 +809,7 @@ func (c *bls12381G1Add) RequiredGas(input []byte) uint64 { | |
return params.Bls12381G1AddGas | |
} | |
-func (c *bls12381G1Add) Run(input []byte) ([]byte, error) { | |
+func (c *bls12381G1Add) Run(input []byte, _ StateDB) ([]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). | |
@@ -785,7 +845,7 @@ func (c *bls12381G1Mul) RequiredGas(input []byte) uint64 { | |
return params.Bls12381G1MulGas | |
} | |
-func (c *bls12381G1Mul) Run(input []byte) ([]byte, error) { | |
+func (c *bls12381G1Mul) Run(input []byte, _ StateDB) ([]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). | |
@@ -837,7 +897,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(input []byte, _ StateDB) ([]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). | |
@@ -883,7 +943,7 @@ func (c *bls12381G2Add) RequiredGas(input []byte) uint64 { | |
return params.Bls12381G2AddGas | |
} | |
-func (c *bls12381G2Add) Run(input []byte) ([]byte, error) { | |
+func (c *bls12381G2Add) Run(input []byte, _ StateDB) ([]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). | |
@@ -920,7 +980,7 @@ func (c *bls12381G2Mul) RequiredGas(input []byte) uint64 { | |
return params.Bls12381G2MulGas | |
} | |
-func (c *bls12381G2Mul) Run(input []byte) ([]byte, error) { | |
+func (c *bls12381G2Mul) Run(input []byte, _ StateDB) ([]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). | |
@@ -972,7 +1032,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(input []byte, _ StateDB) ([]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). | |
@@ -1018,7 +1078,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(input []byte, _ StateDB) ([]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 | |
@@ -1170,7 +1230,7 @@ func (c *bls12381MapG1) RequiredGas(input []byte) uint64 { | |
return params.Bls12381MapG1Gas | |
} | |
-func (c *bls12381MapG1) Run(input []byte) ([]byte, error) { | |
+func (c *bls12381MapG1) Run(input []byte, _ StateDB) ([]byte, error) { | |
// Implements EIP-2537 Map_To_G1 precompile. | |
// > Field-to-curve call expects an `64` bytes input that is interpreted as an element of the base field. | |
// > Output of this call is `128` bytes and is G1 point following respective encoding rules. | |
@@ -1199,7 +1259,7 @@ func (c *bls12381MapG2) RequiredGas(input []byte) uint64 { | |
return params.Bls12381MapG2Gas | |
} | |
-func (c *bls12381MapG2) Run(input []byte) ([]byte, error) { | |
+func (c *bls12381MapG2) Run(input []byte, _ StateDB) ([]byte, error) { | |
// Implements EIP-2537 Map_FP2_TO_G2 precompile logic. | |
// > Field-to-curve call expects an `128` bytes input that is interpreted as an element of the quadratic extension field. | |
// > Output of this call is `256` bytes and is G2 point following respective encoding rules. | |
@@ -1245,7 +1305,7 @@ var ( | |
) | |
// Run executes the point evaluation precompile. | |
-func (b *kzgPointEvaluation) Run(input []byte) ([]byte, error) { | |
+func (b *kzgPointEvaluation) Run(input []byte, _ StateDB) ([]byte, error) { | |
if len(input) != blobVerifyInputLength { | |
return nil, errBlobVerifyInvalidInputLength | |
} | |
@@ -1298,7 +1358,7 @@ func (c *p256Verify) RequiredGas(input []byte) uint64 { | |
} | |
// Run executes the precompiled contract with given 160 bytes of param, returning the output and the used gas | |
-func (c *p256Verify) Run(input []byte) ([]byte, error) { | |
+func (c *p256Verify) Run(input []byte, _ StateDB) ([]byte, error) { | |
// Required input length is 160 bytes | |
const p256VerifyInputLength = 160 | |
// Check the input length |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment