Skip to content

Instantly share code, notes, and snippets.

@tynes
Created September 3, 2024 03:56
Show Gist options
  • Save tynes/ca441c64ddfeb81c13f9b138906620ee to your computer and use it in GitHub Desktop.
Save tynes/ca441c64ddfeb81c13f9b138906620ee to your computer and use it in GitHub Desktop.
gas burn precompile prototype
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