f(eip-8141): implement signer,commit txn

This commit is contained in:
0xjvn 2026-03-05 01:21:03 +05:30
parent 0b11863ec4
commit f69cf10c4d
10 changed files with 607 additions and 8 deletions

View file

@ -556,6 +556,12 @@ func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common
return s.transientStorage.Get(addr, key)
}
// ClearTransientStorage resets all transient storage.
// todo(jvn): Impl it properly
func (s *StateDB) ClearTransientStorage() {
s.transientStorage = newTransientStorage()
}
//
// Setting, updating & deleting state object methods.
//

View file

@ -110,6 +110,10 @@ func (s *hookedStateDB) SetTransientState(addr common.Address, key, value common
s.inner.SetTransientState(addr, key, value)
}
func (s *hookedStateDB) ClearTransientStorage() {
s.inner.ClearTransientStorage()
}
func (s *hookedStateDB) HasSelfDestructed(addr common.Address) bool {
return s.inner.HasSelfDestructed(addr)
}

View file

@ -219,6 +219,29 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
receipt.BlockHash = blockHash
receipt.BlockNumber = blockNumber
receipt.TransactionIndex = uint(statedb.TxIndex())
// EIP-8141: populate frame transaction receipt fields.
if tx.Type() == types.FrameTxType {
receipt.Payer = result.Payer
if result.FrameStatuses != nil {
receipt.FrameReceipts = make([]*types.FrameReceipt, len(result.FrameStatuses))
for i, status := range result.FrameStatuses {
var gasUsed uint64
if i < len(result.FrameGasUsed) {
gasUsed = result.FrameGasUsed[i]
}
var logs []*types.Log
if i < len(result.FrameLogSets) {
logs = result.FrameLogSets[i]
}
receipt.FrameReceipts[i] = &types.FrameReceipt{
Status: uint64(status),
GasUsed: gasUsed,
Logs: logs,
}
}
}
}
return receipt
}

View file

@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)
@ -38,6 +39,12 @@ type ExecutionResult struct {
MaxUsedGas uint64 // Maximum gas consumed during execution, excluding gas refunds.
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
// EIP-8141
Payer *common.Address // Address that paid fees
FrameStatuses []uint8 // Per-frame success (1) or failure (0)
FrameGasUsed []uint64 // Per-frame gas consumed
FrameLogSets [][]*types.Log // Per-frame logs
}
// Unwrap returns the internal evm error which allows us for further
@ -157,6 +164,12 @@ type Message struct {
BlobHashes []common.Hash
SetCodeAuthorizations []types.SetCodeAuthorization
// EIP-8141
IsFrameTx bool // True if this is a frame transaction.
Frames []types.Frame // The frames from Frame Tx.
FrameSigHash common.Hash // pre-computed signing hash
TxFee *big.Int // total fee Todo(jvn): Check correctness
// When SkipNonceChecks is true, the message nonce is not checked against the
// account nonce in state.
//
@ -190,6 +203,44 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
BlobHashes: tx.BlobHashes(),
BlobGasFeeCap: tx.BlobGasFeeCap(),
}
// Handle Frame Transactions
if tx.Type() == types.FrameTxType {
msg.IsFrameTx = true
msg.Frames = tx.Frames()
if sender, ok := tx.FrameSender(); ok {
msg.From = sender
}
// Todo(jvn): Looks like unneccessary port it to here , prev method was clean
// Where you had abstraction on this , Come back later
// Compute effective gas price for TxFee calculation.
effectiveGasPrice := new(big.Int).Set(msg.GasFeeCap)
if baseFee != nil {
tip := new(big.Int).Sub(msg.GasFeeCap, baseFee)
if tip.Cmp(msg.GasTipCap) > 0 {
tip.Set(msg.GasTipCap)
}
effectiveGasPrice = new(big.Int).Add(tip, baseFee)
}
msg.GasPrice = effectiveGasPrice
// TxFee = gasLimit * effectiveGasPrice + blobFees
// Todo(jvn): Check blob fees calc
txFee := new(big.Int).SetUint64(msg.GasLimit)
txFee.Mul(txFee, effectiveGasPrice)
if len(msg.BlobHashes) > 0 && baseFee != nil {
blobGas := uint64(len(msg.BlobHashes) * params.BlobTxBlobGasPerBlob)
blobFee := new(big.Int).SetUint64(blobGas)
blobFee.Mul(blobFee, msg.BlobGasFeeCap)
txFee.Add(txFee, blobFee)
}
msg.TxFee = txFee
msg.FrameSigHash = tx.FrameSigHash()
return msg, nil
}
// If baseFee provided, set gasPrice to effectiveGasPrice.
if baseFee != nil {
msg.GasPrice = msg.GasPrice.Add(msg.GasTipCap, baseFee)
@ -331,10 +382,12 @@ func (st *stateTransition) preCheck() error {
return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit)
}
// Make sure the sender is an EOA
code := st.state.GetCode(msg.From)
_, delegated := types.ParseDelegation(code)
if len(code) > 0 && !delegated {
return fmt.Errorf("%w: address %v, len(code): %d", ErrSenderNoEOA, msg.From.Hex(), len(code))
if !msg.IsFrameTx {
code := st.state.GetCode(msg.From)
_, delegated := types.ParseDelegation(code)
if len(code) > 0 && !delegated {
return fmt.Errorf("%w: address %v, len(code): %d", ErrSenderNoEOA, msg.From.Hex(), len(code))
}
}
}
// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
@ -406,6 +459,19 @@ func (st *stateTransition) preCheck() error {
return fmt.Errorf("%w (sender %v)", ErrEmptyAuthList, msg.From)
}
}
// Frame transactions collect fees after APPROVE opcode, not buyGas.
// Skip balance deduction but still reserve gas from block gas pool.
if msg.IsFrameTx {
if err := st.gp.SubGas(msg.GasLimit); err != nil {
return err
}
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil {
st.evm.Config.Tracer.OnGasChange(0, msg.GasLimit, tracing.GasChangeTxInitialBalance)
}
st.gasRemaining = msg.GasLimit
st.initialGas = msg.GasLimit
return nil
}
return st.buyGas()
}
@ -443,10 +509,23 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
)
// Check clauses 4-5, subtract intrinsic gas if everything is correct
gas, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
txData := msg.Data
if msg.IsFrameTx {
txData, _ = rlp.EncodeToBytes(msg.Frames)
}
gas, err := IntrinsicGas(txData, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
if err != nil {
return nil, err
}
if msg.IsFrameTx {
// Frame tx uses 15000 base cost instead of 21000.
// IntrinsicGas adds 21000, so subtract 6000.
if gas < 6000 {
return nil, ErrGasUintOverflow
}
gas -= 6000
}
if st.gasRemaining < gas {
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas)
}
@ -496,7 +575,9 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
ret []byte
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
)
if contractCreation {
if msg.IsFrameTx {
return st.executeFrameTx(rules)
} else if contractCreation {
ret, _, st.gasRemaining, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining, value)
} else {
// Increment the nonce for the next transaction.
@ -675,3 +756,206 @@ func (st *stateTransition) gasUsed() uint64 {
func (st *stateTransition) blobGasUsed() uint64 {
return uint64(len(st.msg.BlobHashes) * params.BlobTxBlobGasPerBlob)
}
func (st *stateTransition) executeFrameTx(rules params.Rules) (*ExecutionResult, error) {
msg := st.msg
// Validate frame count
if len(msg.Frames) == 0 || len(msg.Frames) > params.FrameTxMaxFrames {
return nil, ErrFrameTxInvalidFormat
}
// Build FrameTxContext
sigHash := msg.FrameSigHash
ftxCtx := &vm.FrameTxContext{
Sender: msg.From,
Nonce: msg.Nonce,
GasTipCap: msg.GasTipCap,
GasFeeCap: msg.GasFeeCap,
BlobFeeCap: msg.BlobGasFeeCap,
BlobVersionedHashes: msg.BlobHashes,
SigHash: sigHash,
TxFee: msg.TxFee,
CurrentFrame: -1, // TODO(jvn) -> change to none?
FrameStatuses: []uint8{},
}
ftxCtx.Frames = make([]vm.FrameInfo, len(msg.Frames))
for i, f := range msg.Frames {
ftxCtx.Frames[i] = vm.FrameInfo{
Mode: f.Mode,
Target: f.Target,
GasLimit: f.GasLimit,
Data: f.Data,
}
}
st.evm.FrameTxCtx = ftxCtx
entryPoint := params.FrameEntryPointAddress
st.state.Snapshot() // tx level snapshot
var (
totalGasUsed uint64
allLogs []*types.Log
frameLogSets [][]*types.Log // per-frame logs for FrameReceipts
frameGasUsedList []uint64 // per-frame gas consumed
// logSizeBefore int // log boundary tracker for per-frame slicing
)
for i, frame := range msg.Frames {
ftxCtx.CurrentFrame = i
// TOdo (chk the bug here)
target := frame.Target
if target == (common.Address{}) {
target = msg.From
}
// Todo(jvn): Look into Impl of clear transient storage , Placing stub gere
st.state.ClearTransientStorage()
// todo (g1): Guess it says to add to accessed address and not to Access list
// add All accessed itens to Prefetcher
senderApprovedBefore := ftxCtx.SenderApproved
payerApprovedBefore := ftxCtx.PayerApproved
payerBefore := ftxCtx.Payer
ftxCtx.ApproveCalledInFrame = false
var (
ret []byte
gasLeft uint64
vmerr error
caller common.Address
)
frameGas := frame.GasLimit
switch frame.Mode {
case types.FrameModeDefault:
caller = entryPoint
st.evm.TxContext.Origin = caller
ret, gasLeft, vmerr = st.evm.Call(
caller, target, frame.Data,
frameGas, new(uint256.Int),
)
case types.FrameModeVerify:
caller = entryPoint
st.evm.TxContext.Origin = caller
ret, gasLeft, vmerr = st.evm.StaticCall(
caller, target, frame.Data, frameGas,
)
case types.FrameModeSender:
if !ftxCtx.SenderApproved {
return nil, ErrFrameTxInvalidApproval
}
caller = msg.From
st.evm.TxContext.Origin = caller
ret, gasLeft, vmerr = st.evm.Call(
caller, target, frame.Data,
frameGas, new(uint256.Int),
)
default:
return nil, ErrFrameTxInvalidFormat
}
// EELS:
// if frame_output.error is not None:
// tx_approval.sender_approved = sender_approved_before
// tx_approval.payer_approved = payer_approved_before
// tx_approval.payer_address = payer_address_before
// tx_approval.approve_called_in_frame = False
if vmerr != nil {
ftxCtx.SenderApproved = senderApprovedBefore
ftxCtx.PayerApproved = payerApprovedBefore
ftxCtx.Payer = payerBefore
ftxCtx.ApproveCalledInFrame = false
}
frameGasUsed := frameGas - gasLeft
totalGasUsed += frameGasUsed
frameStatus := uint8(1)
if vmerr != nil {
frameStatus = 0
}
// TODO(EELS) how approval will be true?
// Ok Approve works like return
if frame.Mode == types.FrameModeVerify && !ftxCtx.ApproveCalledInFrame {
return nil, ErrFrameTxInvalidFrameExecution
}
ftxCtx.FrameStatuses = append(ftxCtx.FrameStatuses, frameStatus)
// Collect per-frame gas used for FrameReceipts
frameGasUsedList = append(frameGasUsedList, frameGasUsed)
var frameLogs []*types.Log
// Todo(jvn): handle Logs Properly
frameLogSets = append(frameLogSets, frameLogs)
allLogs = append(allLogs, frameLogs...)
// jsonBytes, _ := json.MarshalIndent(ftxCtx, "", " ")
// fmt.Printf("FrameCtx JSON after i = %v:", i)
// fmt.Println(string(jsonBytes))
_ = ret
}
if !ftxCtx.PayerApproved {
// Rollback tx-level snapshot
st.state.RevertToSnapshot(0)
return nil, ErrFrameTxInvalidFrameExecution
}
var frameGasSum uint64
for _, f := range msg.Frames {
frameGasSum += f.GasLimit
}
gasLeft := frameGasSum - totalGasUsed
st.gasRemaining -= totalGasUsed
// refund the UNUSED portion to payer
if gasLeft > 0 {
effectivePrice := st.evm.TxContext.GasPrice
refund := new(uint256.Int).SetUint64(gasLeft)
refund.Mul(refund, effectivePrice)
st.state.AddBalance(
ftxCtx.Payer,
refund,
tracing.BalanceIncreaseGasReturn,
)
// Return gas to block gas pool
st.gp.AddGas(gasLeft)
}
// Pay miner tip from gas used
effectiveTip := msg.GasPrice
if rules.IsLondon {
effectiveTip = new(big.Int).Sub(msg.GasPrice, st.evm.Context.BaseFee)
}
if !(st.evm.Config.NoBaseFee && msg.GasFeeCap.Sign() == 0 && msg.GasTipCap.Sign() == 0) {
tip := new(uint256.Int).SetUint64(totalGasUsed)
tipU256, _ := uint256.FromBig(effectiveTip)
tip.Mul(tip, tipU256)
st.state.AddBalance(
st.evm.Context.Coinbase,
tip,
tracing.BalanceIncreaseRewardTransactionFee,
)
}
// Clean up FrameTxCtx after execution completes
defer func() { st.evm.FrameTxCtx = nil }()
payer := ftxCtx.Payer
return &ExecutionResult{
UsedGas: st.gasUsed(),
MaxUsedGas: st.gasUsed(),
Err: nil,
ReturnData: nil,
Payer: &payer,
FrameStatuses: ftxCtx.FrameStatuses,
FrameGasUsed: frameGasUsedList,
FrameLogSets: frameLogSets,
}, nil
}

View file

@ -92,6 +92,9 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
if rules.IsOsaka && tx.Gas() > params.MaxTxGas {
return fmt.Errorf("%w (cap: %d, tx: %d)", core.ErrGasLimitTooHigh, params.MaxTxGas, tx.Gas())
}
if !rules.IsBogota && tx.Type() == types.FrameTxType {
return fmt.Errorf("%w: type %d rejected, pool not yet in Bogota", core.ErrTxTypeNotSupported, tx.Type())
}
// Transactions can't be negative. This may never happen using RLP decoded
// transactions but may occur for transactions created using the RPC.
if tx.Value().Sign() < 0 {

View file

@ -67,6 +67,10 @@ type Receipt struct {
BlobGasUsed uint64 `json:"blobGasUsed,omitempty"`
BlobGasPrice *big.Int `json:"blobGasPrice,omitempty"`
// EIP-8141
Payer *common.Address `json:"payer,omitempty"`
FrameReceipts []*FrameReceipt `json:"frameReceipts,omitempty"`
// Inclusion information: These fields provide information about the inclusion of the
// transaction corresponding to this receipt.
BlockHash common.Hash `json:"blockHash,omitempty"`
@ -74,6 +78,13 @@ type Receipt struct {
TransactionIndex uint `json:"transactionIndex"`
}
// FrameReceipt is the per-frame receipt for frame transactions.
type FrameReceipt struct {
Status uint64 `json:"status"`
GasUsed uint64 `json:"gasUsed"`
Logs []*Log `json:"logs"`
}
type receiptMarshaling struct {
Type hexutil.Uint64
PostState hexutil.Bytes
@ -205,7 +216,7 @@ func (r *Receipt) decodeTyped(b []byte) error {
return errShortTypedReceipt
}
switch b[0] {
case DynamicFeeTxType, AccessListTxType, BlobTxType, SetCodeTxType:
case DynamicFeeTxType, AccessListTxType, BlobTxType, SetCodeTxType, FrameTxType:
var data receiptRLP
err := rlp.DecodeBytes(b[1:], &data)
if err != nil {
@ -368,7 +379,7 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
}
w.WriteByte(r.Type)
switch r.Type {
case AccessListTxType, DynamicFeeTxType, BlobTxType, SetCodeTxType:
case AccessListTxType, DynamicFeeTxType, BlobTxType, SetCodeTxType, FrameTxType:
rlp.Encode(w, data)
default:
// For unsupported types, write nothing. Since this is for

View file

@ -50,6 +50,7 @@ const (
DynamicFeeTxType = 0x02
BlobTxType = 0x03
SetCodeTxType = 0x04
FrameTxType = 0x06
)
// Transaction is an Ethereum transaction.
@ -212,6 +213,8 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
inner = new(BlobTx)
case SetCodeTxType:
inner = new(SetCodeTx)
case FrameTxType:
inner = new(FrameTx)
default:
return nil, ErrTxTypeNotSupported
}

View file

@ -41,6 +41,8 @@ type sigCache struct {
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int, blockTime uint64) Signer {
var signer Signer
switch {
case config.IsBogota(blockNumber, blockTime):
signer = NewBogotaSigner(config.ChainID)
case config.IsPrague(blockNumber, blockTime):
signer = NewPragueSigner(config.ChainID)
case config.IsCancun(blockNumber, blockTime):
@ -70,6 +72,8 @@ func LatestSigner(config *params.ChainConfig) Signer {
var signer Signer
if config.ChainID != nil {
switch {
case config.BogotaTime != nil:
signer = NewBogotaSigner(config.ChainID)
case config.PragueTime != nil:
signer = NewPragueSigner(config.ChainID)
case config.CancunTime != nil:
@ -231,6 +235,9 @@ func newModernSigner(chainID *big.Int, fork forks.Fork) Signer {
if fork >= forks.Prague {
s.txtypes.set(SetCodeTxType)
}
if fork >= forks.Bogota {
s.txtypes.set(FrameTxType)
}
return s
}
@ -262,6 +269,13 @@ func (s *modernSigner) Sender(tx *Transaction) (common.Address, error) {
if tx.ChainId().Cmp(s.chainID) != 0 {
return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainID)
}
// Frame transactions have an explicit sender.
if tt == FrameTxType {
if ftx, ok := tx.inner.(*FrameTx); ok {
return ftx.Sender, nil
}
return common.Address{}, ErrTxTypeNotSupported
}
// 'modern' txs are defined to use 0 and 1 as their recovery
// id, add 27 to become equivalent to unprotected Homestead signatures.
V, R, S := tx.RawSignatureValues()
@ -271,6 +285,9 @@ func (s *modernSigner) Sender(tx *Transaction) (common.Address, error) {
func (s *modernSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
tt := tx.Type()
if tt == FrameTxType {
return nil, nil, nil, nil
}
if !s.supportsType(tt) {
return nil, nil, nil, ErrTxTypeNotSupported
}
@ -290,6 +307,18 @@ func (s *modernSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *bi
return R, S, V, nil
}
// NewBogotaSigner returns a signer that accepts
// - EIP-8141 frame transactions
// - EIP-7702 set code transactions
// - EIP-4844 blob transactions
// - EIP-1559 dynamic fee transactions
// - EIP-2930 access list transactions,
// - EIP-155 replay protected transactions, and
// - legacy Homestead transactions.
func NewBogotaSigner(chainId *big.Int) Signer {
return newModernSigner(chainId, forks.Bogota)
}
// NewPragueSigner returns a signer that accepts
// - EIP-7702 set code transactions
// - EIP-4844 blob transactions

235
core/types/tx_frame.go Normal file
View file

@ -0,0 +1,235 @@
// Copyright 2025 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package types
import (
"bytes"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)
// Frame mode constants as defined by EIP-8141.
const (
FrameModeDefault uint8 = 0 // Regular call with ENTRY_POINT as caller.
FrameModeVerify uint8 = 1 // Validation frame; must call APPROVE; STATICCALL. Data elided from sig hash.
FrameModeSender uint8 = 2 // Regular call with tx.sender as caller; requires sender_approved.
)
// Frame represents a single call frame within a Frame Transaction
type Frame struct {
Mode uint8
Target common.Address
GasLimit uint64
Data []byte
}
// FrameTx represents an EIP-8141 Frame Transaction.
type FrameTx struct {
ChainID *uint256.Int
Nonce uint64
Sender common.Address
Frames []Frame
GasTipCap *uint256.Int // a.k.a. maxPriorityFeePerGas
GasFeeCap *uint256.Int // a.k.a. maxFeePerGas
// Optional blob fields
BlobFeeCap *uint256.Int // a.k.a. maxFeePerBlobGas
BlobHashes []common.Hash // Blob versioned hashes
}
// Todo(jvn): Just a quick version of each fn to fullfill requirements
// Must chk correctness later once impl completes
// copy creates a deep copy of the transaction data.
func (tx *FrameTx) copy() TxData {
cpy := &FrameTx{
Nonce: tx.Nonce,
Sender: tx.Sender,
Frames: make([]Frame, len(tx.Frames)),
ChainID: new(uint256.Int),
GasTipCap: new(uint256.Int),
GasFeeCap: new(uint256.Int),
}
// Deep copy frames.
for i, f := range tx.Frames {
cpy.Frames[i] = Frame{
Mode: f.Mode,
Target: f.Target,
GasLimit: f.GasLimit,
Data: common.CopyBytes(f.Data),
}
}
if tx.ChainID != nil {
cpy.ChainID.Set(tx.ChainID)
}
if tx.GasTipCap != nil {
cpy.GasTipCap.Set(tx.GasTipCap)
}
if tx.GasFeeCap != nil {
cpy.GasFeeCap.Set(tx.GasFeeCap)
}
if tx.BlobFeeCap != nil {
cpy.BlobFeeCap = new(uint256.Int)
cpy.BlobFeeCap.Set(tx.BlobFeeCap)
}
if len(tx.BlobHashes) > 0 {
cpy.BlobHashes = make([]common.Hash, len(tx.BlobHashes))
copy(cpy.BlobHashes, tx.BlobHashes)
}
return cpy
}
// accessors for TxData interface.
func (tx *FrameTx) txType() byte { return FrameTxType }
func (tx *FrameTx) chainID() *big.Int { return tx.ChainID.ToBig() }
func (tx *FrameTx) accessList() AccessList { return nil }
func (tx *FrameTx) data() []byte { return nil }
// todo(jvn) change this
func (tx *FrameTx) gasFeeCap() *big.Int { return tx.GasFeeCap.ToBig() }
func (tx *FrameTx) gasTipCap() *big.Int { return tx.GasTipCap.ToBig() }
func (tx *FrameTx) gasPrice() *big.Int { return tx.GasFeeCap.ToBig() }
func (tx *FrameTx) value() *big.Int { return new(big.Int) } // As Spec Frame txs have no value field Ig , confirm later
func (tx *FrameTx) nonce() uint64 { return tx.Nonce }
func (tx *FrameTx) to() *common.Address { return nil } // No single target; each frame has its own.
// Todo: StreamLine Gas Calc , currently spreadout here and in State Processor
// Port correct Implementation here to keep it clean
func (tx *FrameTx) gas() uint64 {
total := uint64(params.FrameTxIntrinsicGas)
total += tx.CalldataCost()
for _, f := range tx.Frames {
total += f.GasLimit
}
return total
}
func (tx *FrameTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
if baseFee == nil {
return dst.Set(tx.GasFeeCap.ToBig())
}
tip := dst.Sub(tx.GasFeeCap.ToBig(), baseFee)
if tip.Cmp(tx.GasTipCap.ToBig()) > 0 {
tip.Set(tx.GasTipCap.ToBig())
}
return tip.Add(tip, baseFee)
}
func (tx *FrameTx) rawSignatureValues() (v, r, s *big.Int) {
// Frame transactions have no ECDSA signature.
return new(big.Int), new(big.Int), new(big.Int)
}
func (tx *FrameTx) setSignatureValues(chainID, v, r, s *big.Int) {
// Frame transactions don't have signatures.
tx.ChainID = uint256.MustFromBig(chainID)
}
func (tx *FrameTx) encode(b *bytes.Buffer) error {
return rlp.Encode(b, tx)
}
func (tx *FrameTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}
// sigHash computes the signature hash for a frame transaction.
func (tx *FrameTx) sigHash(chainID *big.Int) common.Hash {
elidedFrames := make([]interface{}, len(tx.Frames))
for i, f := range tx.Frames {
frameData := f.Data
if f.Mode == FrameModeVerify {
frameData = []byte{} // Elide data for VERIFY frames.
}
elidedFrames[i] = []interface{}{
f.Mode,
f.Target,
f.GasLimit,
frameData,
}
}
return prefixedRlpHash(
FrameTxType,
[]interface{}{
chainID,
tx.Nonce,
tx.Sender,
elidedFrames,
tx.GasTipCap,
tx.GasFeeCap,
tx.BlobFeeCap,
tx.BlobHashes,
})
}
// ComputeSigHash returns the signature hash for external use.
func (tx *FrameTx) ComputeSigHash() common.Hash {
return tx.sigHash(tx.ChainID.ToBig())
}
// TotalFrameGas returns the sum of gas limits across all frames.
func (tx *FrameTx) TotalFrameGas() uint64 {
var total uint64
for _, f := range tx.Frames {
total += f.GasLimit
}
return total
}
// CalldataCost returns the gas cost for the calldata equivalent in the frame transaction.
func (tx *FrameTx) CalldataCost() uint64 {
encodedFrames, err := rlp.EncodeToBytes(tx.Frames)
if err != nil {
return 0
}
return calldataCost(encodedFrames)
}
// Check this dont think Currently using this anywhere
func calldataCost(data []byte) uint64 {
z := uint64(bytes.Count(data, []byte{0}))
nz := uint64(len(data)) - z
return z*params.TxDataZeroGas + nz*params.TxDataNonZeroGasEIP2028
}
// FrameSigHash returns the signature hash
func (tx *Transaction) FrameSigHash() common.Hash {
if ftx, ok := tx.inner.(*FrameTx); ok {
return ftx.ComputeSigHash()
}
return common.Hash{}
}
// Frames returns the frame list for frame transactions, nil otherwise.
func (tx *Transaction) Frames() []Frame {
if ftx, ok := tx.inner.(*FrameTx); ok {
return ftx.Frames
}
return nil
}
// FrameSender returns the explicit sender for frame transactions.
func (tx *Transaction) FrameSender() (common.Address, bool) {
if ftx, ok := tx.inner.(*FrameTx); ok {
return ftx.Sender, true
}
return common.Address{}, false
}

View file

@ -296,6 +296,7 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase
}
func (miner *Miner) commitTransaction(env *environment, tx *types.Transaction) error {
// Todo(jvn): Maybe add frame Txn handling here?
if tx.Type() == types.BlobTxType {
return miner.commitBlobTransaction(env, tx)
}