mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-19 11:20:45 +00:00
f(eip-8141): implement signer,commit txn
This commit is contained in:
parent
0b11863ec4
commit
f69cf10c4d
10 changed files with 607 additions and 8 deletions
|
|
@ -556,6 +556,12 @@ func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common
|
||||||
return s.transientStorage.Get(addr, key)
|
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.
|
// Setting, updating & deleting state object methods.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,10 @@ func (s *hookedStateDB) SetTransientState(addr common.Address, key, value common
|
||||||
s.inner.SetTransientState(addr, key, value)
|
s.inner.SetTransientState(addr, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) ClearTransientStorage() {
|
||||||
|
s.inner.ClearTransientStorage()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *hookedStateDB) HasSelfDestructed(addr common.Address) bool {
|
func (s *hookedStateDB) HasSelfDestructed(addr common.Address) bool {
|
||||||
return s.inner.HasSelfDestructed(addr)
|
return s.inner.HasSelfDestructed(addr)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,29 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
|
||||||
receipt.BlockHash = blockHash
|
receipt.BlockHash = blockHash
|
||||||
receipt.BlockNumber = blockNumber
|
receipt.BlockNumber = blockNumber
|
||||||
receipt.TransactionIndex = uint(statedb.TxIndex())
|
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
|
return receipt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -38,6 +39,12 @@ type ExecutionResult struct {
|
||||||
MaxUsedGas uint64 // Maximum gas consumed during execution, excluding gas refunds.
|
MaxUsedGas uint64 // Maximum gas consumed during execution, excluding gas refunds.
|
||||||
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
|
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)
|
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
|
// Unwrap returns the internal evm error which allows us for further
|
||||||
|
|
@ -157,6 +164,12 @@ type Message struct {
|
||||||
BlobHashes []common.Hash
|
BlobHashes []common.Hash
|
||||||
SetCodeAuthorizations []types.SetCodeAuthorization
|
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
|
// When SkipNonceChecks is true, the message nonce is not checked against the
|
||||||
// account nonce in state.
|
// account nonce in state.
|
||||||
//
|
//
|
||||||
|
|
@ -190,6 +203,44 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
|
||||||
BlobHashes: tx.BlobHashes(),
|
BlobHashes: tx.BlobHashes(),
|
||||||
BlobGasFeeCap: tx.BlobGasFeeCap(),
|
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 provided, set gasPrice to effectiveGasPrice.
|
||||||
if baseFee != nil {
|
if baseFee != nil {
|
||||||
msg.GasPrice = msg.GasPrice.Add(msg.GasTipCap, baseFee)
|
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)
|
return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit)
|
||||||
}
|
}
|
||||||
// Make sure the sender is an EOA
|
// Make sure the sender is an EOA
|
||||||
code := st.state.GetCode(msg.From)
|
if !msg.IsFrameTx {
|
||||||
_, delegated := types.ParseDelegation(code)
|
code := st.state.GetCode(msg.From)
|
||||||
if len(code) > 0 && !delegated {
|
_, delegated := types.ParseDelegation(code)
|
||||||
return fmt.Errorf("%w: address %v, len(code): %d", ErrSenderNoEOA, msg.From.Hex(), len(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)
|
// 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)
|
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()
|
return st.buyGas()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -443,10 +509,23 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
if st.gasRemaining < gas {
|
||||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, 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
|
ret []byte
|
||||||
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
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)
|
ret, _, st.gasRemaining, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining, value)
|
||||||
} else {
|
} else {
|
||||||
// Increment the nonce for the next transaction.
|
// Increment the nonce for the next transaction.
|
||||||
|
|
@ -675,3 +756,206 @@ func (st *stateTransition) gasUsed() uint64 {
|
||||||
func (st *stateTransition) blobGasUsed() uint64 {
|
func (st *stateTransition) blobGasUsed() uint64 {
|
||||||
return uint64(len(st.msg.BlobHashes) * params.BlobTxBlobGasPerBlob)
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,9 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
||||||
if rules.IsOsaka && tx.Gas() > params.MaxTxGas {
|
if rules.IsOsaka && tx.Gas() > params.MaxTxGas {
|
||||||
return fmt.Errorf("%w (cap: %d, tx: %d)", core.ErrGasLimitTooHigh, params.MaxTxGas, tx.Gas())
|
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 can't be negative. This may never happen using RLP decoded
|
||||||
// transactions but may occur for transactions created using the RPC.
|
// transactions but may occur for transactions created using the RPC.
|
||||||
if tx.Value().Sign() < 0 {
|
if tx.Value().Sign() < 0 {
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,10 @@ type Receipt struct {
|
||||||
BlobGasUsed uint64 `json:"blobGasUsed,omitempty"`
|
BlobGasUsed uint64 `json:"blobGasUsed,omitempty"`
|
||||||
BlobGasPrice *big.Int `json:"blobGasPrice,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
|
// Inclusion information: These fields provide information about the inclusion of the
|
||||||
// transaction corresponding to this receipt.
|
// transaction corresponding to this receipt.
|
||||||
BlockHash common.Hash `json:"blockHash,omitempty"`
|
BlockHash common.Hash `json:"blockHash,omitempty"`
|
||||||
|
|
@ -74,6 +78,13 @@ type Receipt struct {
|
||||||
TransactionIndex uint `json:"transactionIndex"`
|
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 receiptMarshaling struct {
|
||||||
Type hexutil.Uint64
|
Type hexutil.Uint64
|
||||||
PostState hexutil.Bytes
|
PostState hexutil.Bytes
|
||||||
|
|
@ -205,7 +216,7 @@ func (r *Receipt) decodeTyped(b []byte) error {
|
||||||
return errShortTypedReceipt
|
return errShortTypedReceipt
|
||||||
}
|
}
|
||||||
switch b[0] {
|
switch b[0] {
|
||||||
case DynamicFeeTxType, AccessListTxType, BlobTxType, SetCodeTxType:
|
case DynamicFeeTxType, AccessListTxType, BlobTxType, SetCodeTxType, FrameTxType:
|
||||||
var data receiptRLP
|
var data receiptRLP
|
||||||
err := rlp.DecodeBytes(b[1:], &data)
|
err := rlp.DecodeBytes(b[1:], &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -368,7 +379,7 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
|
||||||
}
|
}
|
||||||
w.WriteByte(r.Type)
|
w.WriteByte(r.Type)
|
||||||
switch r.Type {
|
switch r.Type {
|
||||||
case AccessListTxType, DynamicFeeTxType, BlobTxType, SetCodeTxType:
|
case AccessListTxType, DynamicFeeTxType, BlobTxType, SetCodeTxType, FrameTxType:
|
||||||
rlp.Encode(w, data)
|
rlp.Encode(w, data)
|
||||||
default:
|
default:
|
||||||
// For unsupported types, write nothing. Since this is for
|
// For unsupported types, write nothing. Since this is for
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ const (
|
||||||
DynamicFeeTxType = 0x02
|
DynamicFeeTxType = 0x02
|
||||||
BlobTxType = 0x03
|
BlobTxType = 0x03
|
||||||
SetCodeTxType = 0x04
|
SetCodeTxType = 0x04
|
||||||
|
FrameTxType = 0x06
|
||||||
)
|
)
|
||||||
|
|
||||||
// Transaction is an Ethereum transaction.
|
// Transaction is an Ethereum transaction.
|
||||||
|
|
@ -212,6 +213,8 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
|
||||||
inner = new(BlobTx)
|
inner = new(BlobTx)
|
||||||
case SetCodeTxType:
|
case SetCodeTxType:
|
||||||
inner = new(SetCodeTx)
|
inner = new(SetCodeTx)
|
||||||
|
case FrameTxType:
|
||||||
|
inner = new(FrameTx)
|
||||||
default:
|
default:
|
||||||
return nil, ErrTxTypeNotSupported
|
return nil, ErrTxTypeNotSupported
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ type sigCache struct {
|
||||||
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int, blockTime uint64) Signer {
|
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int, blockTime uint64) Signer {
|
||||||
var signer Signer
|
var signer Signer
|
||||||
switch {
|
switch {
|
||||||
|
case config.IsBogota(blockNumber, blockTime):
|
||||||
|
signer = NewBogotaSigner(config.ChainID)
|
||||||
case config.IsPrague(blockNumber, blockTime):
|
case config.IsPrague(blockNumber, blockTime):
|
||||||
signer = NewPragueSigner(config.ChainID)
|
signer = NewPragueSigner(config.ChainID)
|
||||||
case config.IsCancun(blockNumber, blockTime):
|
case config.IsCancun(blockNumber, blockTime):
|
||||||
|
|
@ -70,6 +72,8 @@ func LatestSigner(config *params.ChainConfig) Signer {
|
||||||
var signer Signer
|
var signer Signer
|
||||||
if config.ChainID != nil {
|
if config.ChainID != nil {
|
||||||
switch {
|
switch {
|
||||||
|
case config.BogotaTime != nil:
|
||||||
|
signer = NewBogotaSigner(config.ChainID)
|
||||||
case config.PragueTime != nil:
|
case config.PragueTime != nil:
|
||||||
signer = NewPragueSigner(config.ChainID)
|
signer = NewPragueSigner(config.ChainID)
|
||||||
case config.CancunTime != nil:
|
case config.CancunTime != nil:
|
||||||
|
|
@ -231,6 +235,9 @@ func newModernSigner(chainID *big.Int, fork forks.Fork) Signer {
|
||||||
if fork >= forks.Prague {
|
if fork >= forks.Prague {
|
||||||
s.txtypes.set(SetCodeTxType)
|
s.txtypes.set(SetCodeTxType)
|
||||||
}
|
}
|
||||||
|
if fork >= forks.Bogota {
|
||||||
|
s.txtypes.set(FrameTxType)
|
||||||
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -262,6 +269,13 @@ func (s *modernSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||||
if tx.ChainId().Cmp(s.chainID) != 0 {
|
if tx.ChainId().Cmp(s.chainID) != 0 {
|
||||||
return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainID)
|
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
|
// 'modern' txs are defined to use 0 and 1 as their recovery
|
||||||
// id, add 27 to become equivalent to unprotected Homestead signatures.
|
// id, add 27 to become equivalent to unprotected Homestead signatures.
|
||||||
V, R, S := tx.RawSignatureValues()
|
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) {
|
func (s *modernSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||||
tt := tx.Type()
|
tt := tx.Type()
|
||||||
|
if tt == FrameTxType {
|
||||||
|
return nil, nil, nil, nil
|
||||||
|
}
|
||||||
if !s.supportsType(tt) {
|
if !s.supportsType(tt) {
|
||||||
return nil, nil, nil, ErrTxTypeNotSupported
|
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
|
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
|
// NewPragueSigner returns a signer that accepts
|
||||||
// - EIP-7702 set code transactions
|
// - EIP-7702 set code transactions
|
||||||
// - EIP-4844 blob transactions
|
// - EIP-4844 blob transactions
|
||||||
|
|
|
||||||
235
core/types/tx_frame.go
Normal file
235
core/types/tx_frame.go
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -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 {
|
func (miner *Miner) commitTransaction(env *environment, tx *types.Transaction) error {
|
||||||
|
// Todo(jvn): Maybe add frame Txn handling here?
|
||||||
if tx.Type() == types.BlobTxType {
|
if tx.Type() == types.BlobTxType {
|
||||||
return miner.commitBlobTransaction(env, tx)
|
return miner.commitBlobTransaction(env, tx)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue