mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-19 19:30:44 +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)
|
||||
}
|
||||
|
||||
// ClearTransientStorage resets all transient storage.
|
||||
// todo(jvn): Impl it properly
|
||||
func (s *StateDB) ClearTransientStorage() {
|
||||
s.transientStorage = newTransientStorage()
|
||||
}
|
||||
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) ClearTransientStorage() {
|
||||
s.inner.ClearTransientStorage()
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) HasSelfDestructed(addr common.Address) bool {
|
||||
return s.inner.HasSelfDestructed(addr)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
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 {
|
||||
// Todo(jvn): Maybe add frame Txn handling here?
|
||||
if tx.Type() == types.BlobTxType {
|
||||
return miner.commitBlobTransaction(env, tx)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue